Automated Coder

Exploring the Code of CruiseControl.Net

Archive for February, 2009

More Playing Around

Posted by Craig Sutherland on 28 February, 2009

This Time the Web

I’ve gotten some good feedback on the changes to CCTray to allowing displaying the status of a build (right down to task level), so I thought I’d see if I could quickly build a similar interface for the dashboard.

Now, I say similar, as I don’t want to spend a lot of time and effort trying to make it look identical, plus there are some nice things I can do with a web interface that are hard in WinForms. So, here is what I’ve come up with so far.

The new display is accessed via the project menu – there is a new link called “Project Status”. Clicking on this link brings up a display like the following:

WebStatus1

Note, this is using a custom theme – I’ve changed to using this theme for my servers, but it will still work in the standard look.

Like the CCTray version it shows all the items in a build – and has a hierarchy for them (e.g. tasks are under build tasks, publishers under publisher tasks, etc.)

This display will be updated automatically (currently every ten seconds but I may make this configurable). Additionally it’s using AJAX, so it provides a smoother experience (although it still needs some fine tuning).

WebStatus2

Like the windows version it is possible to drill down for more details. This is done by clicking on an item:

WebStatus3

Multiple items can be opened at once. Unlike the windows version, the details are displayed underneath and only the available details are displayed – everything else is hidden.

One other major difference is a lack of progress bar. I am thinking about adding one, but I haven’t decided on the best way to implement it yet (the windows version is on a per-item basis).

So, still a work in progress, but slowly shaping into something that will be useful :-)

What do you think so far?

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

Playing Around

Posted by Craig Sutherland on 27 February, 2009

It’s Down-time

Dave, the project owner for CruiseControl.Net has returned from his holiday and we are hoping to get the next release of CruiseControl.Net (1.4.3) out soon. To help with this we’ve decided to have a change freeze (we did some more fixing of issues while he was away) to ensure we have a stable code base for release.

I’ve also decided to pause on the messaging and communications client work I’ve done – otherwise things are starting to get too messy :-( As such I’ve got some down-time until the 1.4.3 release goes out (yes, i could work on merging the security changes, but I prefer it when I have a target to hit, rather than vague promises of soon).

So, I thought I’d take a quick look at an idea that’s been bouncing around in my head for a while.

The Current CCTray

I’m sure a lot of people have seen that their project is building, so they open up CCTray and get a view like the following:

Status-1

This tells us that the project is building – but not much else!

For example, what is the current task? Has anything failed in the build? Has the source been fetched yet? Etc, etc, etc.

The dashboard is a little bit better – at least it tells us what is running at the moment. But there’s no history of what’s happened (unless we look at the logs) – plus it’s still very high-level.

Instead, I’d like a display that shows everything that is in the project – plus what has happened, what is currently happening and what is due to happen. It would also be nice to get an estimate of how much longer, plus some details on the current item.

My Rough Prototype

So, I’ve put together a rough prototype of how this could work (and look). Here are some screenshots of how this would look in CCTray – bear in mind that this is at a very early stage, it still needs a lot of tidying up :-)

First off, here is a general view of the project status:

Status-2

This shows a snapshot of the last run of the project. In this case there were no changes, so most of the tasks were cancelled – as was the overall build.

When a build starts, the status will be updated, like follows:

Status-3

There’s a gray bar across the top – this will eventually progress as the build is running (at the moment I’ve hard-coded it to 50%).

Clicking on an item will drill down into the details – including (hopefully) the progress through the item:

Status-4

Finally, when everything has completed, the statuses will be updated and the progress bar colour changed to green (for success) or red (for failure).

Status-5

Eventually I’ll also expand it out to the dashboard (that’s a bit harder to do the automatic refreshing). Also the framework is in place for allowing items to define their own details – so the display will become more meaningful.

Anyway, this is just a quick sneak peak at what I’m working on – what do you think of it?

UPDATE

One of the things I didn’t mention in this post, is these new screens are part of CCTray – not a new application (or part of Project Leo). They will be accessed by right-clicking on a project in the list and select Current Status (selecting a project and pressing F4 will do the same).

Posted in CruiseControl.Net | Tagged: | 3 Comments »

Inner Workings: Project Physiology

Posted by Craig Sutherland on 26 February, 2009

Breaking Down the Project

Previously I have covered how a project build is scheduled (read it here). In the previous post I covered how the scheduling mechanism works, how it ties in queues and project integrators, and then I stopped at the point of a project integrating.

In this post, I’ll dive into the workings of a project and how its build process actually works.

Starting Where I Stopped: Integrate()

In the previous post, I showed the logic down to the point of calling Integrate() on an IProject. This method takes in an IntegrationRequest and returns an IIntegrationResult. And that’s where we enter another rabbit hole!

The integration process is split between two classes – Project and IntegrationRunner, with a little help from IntegrationResultManager on the side. Project is the implementation of IProject – it contains all the configuration and does the actual work. IntegrationRunner handles the coordination and ties everything together. Finally IntegrationResultManager is a persistence store – it allows information from one build to be persisted until the next – it is a wrapper around the state manager in the project, plus it provides a little bit of extra functionality.

So, back to the plot: Integrate() has been called on Project, what happens next? Very simply, Project hands over control to IntegrationRunner by calling its Integrate() method. This method actually does some work.

First, it calls StartNewIntegration() on the IntegrationResultManager. This loads a new IntegrationResult with all the relevant details (project name, working and artefact directories, request details, summary of last build and Web URL). This instance will then be passed around and updated by the various child processes.

With the result now initialised, IntegrationRunner makes sure the working and artefact directories exist. After this step, the build officially begins – as this is where the timer is now started. After this, things really start happening! In order:

  1. Check for any modifications
  2. Check if a build should start
  3. Generate the label
  4. Build
  5. Post-build
  6. Clean up

Build Process

So let’s see how each of these steps work.

Checking for Modifications

Checking for modifications uses a combination of QuietPeriod and an implementation of ISourceControl. Without going too in-depth (this will be another post), QuietPeriod retrieves any modifications from the ISourceControl implementation, does a few checks to validate them, and then returns the list.

At this point, I should note that every project has a source control block, even those that have not been configured with one. In this case the source control block is NullSourceControl. This block will always return an empty list.

These modifications are then stored in the integration result.

Checking to Start a Build

In my previous post I talked about scheduling a build. This was kind-of a simplification, because what the scheduler actually does is schedule a source control check. At this point the build process is still in an undecided state – should it continue or should it stop?

The integration result contains a method called ShouldRunBuild(). This checks to see if any modifications were detected, or a force build has been requested. If either of these conditions are met, then a build is started, otherwise the entire process is stopped and the runner goes straight to the clean-up step.

Generating the Label

Generating the label is triggered by IntegrationRunner, control gets passed to the Project, which then passes it on to the actual labeller (an instance of ILabeller). Each and build requires a label – but the actual label can differ according to the labeller.

If the project does not manually define a labeller (i.e. in the configuration), then an instance of DefaultLabeller is used instead. This merely increments a build number every time (optionally adding a prefix, a suffix or both).

Building

Building involves three steps – a pre-build, getting the source and then the actual build.

Pre-build is run first – and it involves calling Prebuild() on Project. This merely iterates through all the tasks in the prebuild block. If any tasks fail, then the entire pre-build is cancelled, and a failure result is returned.

Getting the source involves calling the GetSource() method on the source control implementation. Once again this is implementation specific, so I won’t cover it in any detail The main thing to note is getting the source does not return a success/failure value – so the only way to cancel the build process from the source control operation is to throw an exception!

After the source code has been fetched, the runner then calls Build() on Project. This is the same as PreBuild(), except it iterates over the build tasks. Likewise, if any tasks fail, then the build is cancelled, but this time no result is returned. So again, the only way to cancel the process is to throw an exception.

Once all of these tasks have finished, the source code is labelled. This merely involves calling LabelSourceControl() on the source control implementation. Thus it is up to the implementation on what level of source control labelling is allowed, and what options are exposed.

Running the Post-Build

Post-build is very similar to pre-build and build. This is also a method on Project (PublishResults()), but there is one major difference – if a post-build task fails, the entire process is not cancelled. This means if one publisher fails, it will merely log the error, and then continue onto the next publisher.

At the end of the publishers, it checks to see if the build has succeeded. If not it adds all the people who broke the build to the messages. This merely involves loading all the people who submitted changes since the previous build into the messages array, plus anybody who broke it the last time.

Cleaning Up

The very last stage of a build is cleaning up. This merely involves setting the project status back to sleeping. This step is always done, no matter what else was done during the build.

Project Status

During this process there are a few projects statuses that it goes through. These are set in the Activity property of the Project. This status is changed at the following points:

  • Check for modifications – changes to CheckingModifications
  • Build – changes to Building
  • Clean up – changes to sleeping

As you can see there are a few gaps between the project status and the actual work happening.

Summary

This has covered the general process of how a build runs.

The build is a fairly simple, linear process – check for modifications, check to build, generate label, build, publish, clean up.

The IntegrationRunner acts as a coordinator for the whole process and controls the sequence of steps. The actual work is handled by passing off control to either the Project or one of the configured elements (e.g. labeller, source control, etc.)

Pre-build, build and post-build are all similar – they run a block of tasks/publishers. The only difference is post-build doesn’t cancel when an error occurs.

Hope this helps clear up the process of how a build works :-)

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

Simplifying Communications, Part 2

Posted by Craig Sutherland on 25 February, 2009

The Story Continues, Again…

In my last post (here) I modified the server in preparation for building a communications client. This involved adding a new method to handle “generic” messages. This method took in an action name and a request message, and then returns a response.

Now it’s time to build on this and start putting together a communications client.

New vs. Old

The first question is where will the communications code go? At the moment there are two choices – Remote or a new project.

Remote is currently the communications library within CruiseControl.Net, but it also handles a number of other items (e.g. exceptions, events, interfaces, etc.) Additionally it depends on the NetReflector library.

Adding a new project would mean potential duplication, although it would be smaller!

In the end I decided to add a new project in the same folder and link to the existing files. The new project has the same namespace (although a different assembly name). Additionally I modified a couple of files to include compiler directives (#if…#endif) to exclude the NetReflector classes – this removes the dependency on NetReflector.

Now I can use the same classes, but without all the extra baggage. Additionally I can include the new communications classes in Remote later on if I think they are necessary for the main server.

The Design

First off, I want a generic interface to the communications. This will be a single class that handles all communications calls – this way the application does not need to know whether it is HTTP, .NET Remoting or something else.

Secondly, there will be a number of “transport” channels. Each one of these channels contains the actual communications details (e.g. HTTP, .NET Remoting, etc). When the application wants a new client, it instantiates the channel, start a new client and links the two together.

Finally, to simplify the initial set-up, there will be a factory class to do the above step – plus handle some extra steps (auto-detection of the protocol, etc.)

The following diagram shows the various components:

Communications Client

Implementation

The code consists of five classes and one interface. These are:

  • IServerConnection
  • RemotingConnection
  • HttpConnection
  • CruiseServerClient
  • CommunicationsException
  • CruiseServerClientFactory

Most of these should be straight-forward :-)

IServerConnection

IServerConnection defines the channel to the client. It has one method – SendMessage() and one property – ServerName. ServerName is the name of the server that the connection is for. This is used to set the Server property on the request (since this required for web connections). SendMessage() does the actual sending of the message. The input arguments are the action and a ServerRequest, and it returns a Response.

This is all it does, it doesn’t need to perform any validation of the response, as this will be handled by the client.

RemotingConnection and HttpConnection

These are two implementations of IServerConnection – one for .Net Remoting and the other for HTTP. They handle connecting to the server, converting the messages into wire formats and then converting back again.

RemotingConnection is very simple – the only fancy logic in it is generating the URI for the remote server (and even so, is still very simple).

HttpConnection does a bit more work as it converts the request message to XML and then has to convert the response from XML. Internally it uses a WebClient instance, and just calls UploadValues() to send the data (this is a POST request).

CruiseServerClient

This is the biggest of the classes. Each available action on the server has a matching method here. CruiseServerClient takes the incoming arguments and puts them into a request message (of the correct type). This message then gets passed onto the IServerConnection instance, which sends it to the server and returns a response.

When the response is returned CruiseServerClient validates the response. If the response has a status of failed, it will throw an exception (see CommunicationsException below), otherwise it will return the data items (if any).

This class has similar helper methods to what was added to CruiseManager – GetServerRequest(), GetProjectRequest() and ValidateResponse() – to help with this processing.

It also allows the calling application to set a target server. If a target server is not set, then the server name from the server connection is used. Otherwise the target name is used. This is required for HTTP calls where the target server and the connection server can be different.

CommunicationsException

A CommunicationsException is thrown whenever the remote server returns a failure response. This encapsulates the errors and makes it easier for the calling application to differentiate between errors from the remote server and errors on the client side.

CruiseServerClientFactory

The final class is CruiseServerClientFactory. This provides helper methods for starting a new client. Currently there are helper methods for starting a .NET Remoting connection, an HTTP connection and a generic connection.

The generic connection will attempt to detect the connection type from the URI. If the URI starts with http://, then an HTTP connection is started. If the URI starts with tcp:// then a .NET Remoting connection is started. Anything else throws an exception.

This class can be bypassed and the calling application can start its own IServerConnection and wire it to a CruiseServerClient. But this class will help reduce coding in most situations.

An Example

Now that I’ve built the library, now is it used?

First, add the assembly ThoughtWorks.CruiseControl.Communications to the calling project. Just remember that the namespace for this library is actually ThoughtWorks.CruiseControl.Remote – not Communications.

Then instantiate a client. The following shows a few different approaches:

// Generate an HTTP client that targets another server
CruiseServerClient client = CruiseServerClientFactory
 .GenerateHttpClient("http://buildserver/ccnet", "actualserver");
// Generate a .NET remoting client explicity
CruiseServerClient client = CruiseServerClientFactory
 .GenerateRemotingClient("buildserver");
// Generate a .NET remoting client implicity (by auto-detection)
CruiseServerClient client = CruiseServerClientFactory
 .GenerateClient("tcp://buildserver:21234");

To then call the server is as simple as the following:

client.ForceBuild("ProjectName");

And that’s it. Any errors will be raised as exceptions, so make sure there is a try-catch block somewhere around the code.

Where To From Here?

What I have done so far is the basic version of the communications client, which covers most of what the current code can do. Now it’s time to start expanding it to cover other scenarios, e.g. security, handling proxies, other communications channels, etc.

Also, I haven’t covered it in this post, but I have updated my CCCmd (command-line remote client) interface, so next step is to document it and include it in the main code base. I’ll also look at CCTray, the dashboard and the triggers and publishers that connect to other servers.

Another area is to make custom versions of this library for different platforms – e.g. Silverlight, Compact framework, etc. However for Silverlight it needs an asynchronous model, and I haven’t figured out what is the best way to do that yet.

Finally, I’m toying with the idea of making the communications library backwards compatible (e.g able to connect to 1.4.3 servers or earlier). Unfortunately this will require a lot more work as the older versions don’t have the generic message handler :-( So this one might go in the too hard basket.

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

Simplifying Communications, Part 1

Posted by Craig Sutherland on 25 February, 2009

The Story Continues…

Recently I made some changes to how ICruiseManager works. ICruiseManager defines the communications interface between the clients and the server.

My changes were to modify ICruiseManager (and ICruiseServer) to use a messaging-based interface, rather than the old-style of arguments (and sometimes multiple overloads of the same method). This means every method in ICruiseManager now receives a common set of information, and returns a common set of information.

While this means all the methods are consistent, it also had the download of making using ICruiseManager more complex and breaking backwards compatibility. In this post I’ll start to look at remedying these two issues.

Old vs. News

The following is an example of a method in the old interface:

void ForceBuild(string projectName, string enforcerName);

Compared with how it now looks:

Response ForceBuild(ProjectRequest request);

All of the arguments are now self-contained with the message, but they are all in there (although enforcer name now comes from the session instead).

With this understanding, it is very easy to build a conversion layer:

Response resp = cruiseServer.ForceBuild(GenerateProjectRequest(projectName));
ValidateResponse(resp);

This converts the old-style arguments into a message-style request, and then checks the outcome to ensure everything is ok.

To achieve this, I have added a couple of helper methods:

  • GenerateProjectRequest()/GenerateServerRequest() – takes in the incoming arguments and generates a valid request
  • ValidateResponse() – checks the response, if the response is a failure then an exception is thrown

I then went through and added all the old methods to CruiseManager and converted the calls to the new format. Finally, I renamed ICruiseManager to ICruiseManager2 and re-added the old interface (I also modified CruiseManager to implement both).

Now the server can handle connections from old versions of CCTray and web dashboard, while at the same time it can handle new-style calls.

One Call to Rule Them All

The second server change I want to make is to simplify the number of methods. Originally I had planned to make a client that exposed each and every method, with each and every method needing to be implemented in the communications channels. Currently, there are 34 methods – which meant I would have to implement 102 methods (generic, HTTP and .NET Remoting). Each new communications channel would require 34 methods, and if I added new methods that’s three places (or more) to change!

Yuck! :-(

Instead I wanted a nice simple way – with only one method in the channels (the generic interface will still have a lot of methods). In the end I decided I could use the messaging format to my advantage.

Every request message derives from ServerRequest, while every response derives from Response – so I can guarantee that these two messages will be passed. Additionally only one of each message will be passed – one request in, one response out. Finally the ToString() method on each message always returns XML.

So far, so good :-)

Next, each method name is unique within ICruiseManager2 – there are no duplicates (overloads are handled by passing in different requests and the code works out what to do based on the request type). This means if I pass the method name as a string, I could easily get the method using reflection!

With this information I can now build a very simple calling method to handle any type of request call. The signature for this method is:

string ProcessMessage(string action, string message);

This very simply takes in a request message as a string, and returns the response message as a string. The final part to make this method work is the action argument – this is the name of the method that will be called.

The implementation is also fairly simple, although a little more complexity is required:

  1. Load the XML into an XmlDocument
  2. From the document get the message type
  3. Find the method from the action (via reflection)
  4. Convert the request into an instance of the message type
  5. Invoke the method (again via reflection)
  6. Convert the response into XML and return it

Step 2 involves a little bit of reflection as well – it checks all the types in the Remote library and loads any that have an XmlRootAttribute into a dictionary. This dictionary is then used for returning the actual message type.

Step 4 involves the type from step 2 and an XmlSerializer – so there may be a performance hit here. To reduce this performance hit I cache all the serialisers (yes I could have pre-generated them as well, but I’m trying to reduce the number of assemblies where possible).

Additionally I wrapped all of this logic in a try-catch block. If an exception occurs (i.e. the message or action is invalid), then a response is still generated – with the exception details in it.

Part 1 Completed

That concludes the changes to the server-side. These have set things up to:

  1. Handle old CruiseControl.Net clients
  2. Take a single message and route it to the correct handler

Now I’m ready to try and build a communications clients – stay tuned :-)

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

Inner Workings: The Project Scheduler

Posted by Craig Sutherland on 20 February, 2009

The Heart of the Matter

The core of CruiseControl.Net is the project scheduler. This is the piece of code that is responsible for scheduling builds – without which CruiseControl.Net just wouldn’t work.

Before I delve into the actual workings of the scheduler, let’s quickly review how builds can be scheduled.

First and foremost, in order to schedule a project build a trigger is required. There are a number of different types of trigger – from interval triggers to scheduled triggers to projects that monitor other locations or projects. Additionally triggers can be combined or filtered. But one thing all filters have in common is they tell the scheduler a build needs to be performed.

The second part of the scheduler is the queues. Early versions of CruiseControl.Net had each project running in its own little world – they didn’t affect other projects. As the number of projects increased, this lead to increased contention on the build servers and a lack of resources. To counter this queues were added. A queue is a group of projects, of which only one can have a running build at any point in time. Generally they work on a first-in, first-out basis, but it is possible to set queue priorities.

With this background, let’s delve into how project scheduling actually works

Managers, Integrators and Queues

The main class that handles everything in CruiseControl.Net is CruiseServer. This is responsible for starting everything and handling all user interactions. But, it doesn’t actually handle the scheduling of builds. This is handled by a number of other classes.

Looking at CruiseServer, there is a IntegrationQueueManager class. This encapsulates all the actual projects and their integrators. Now an integrator implements IProjectIntegrator and is responsible for the actual triggering of a build. In a moment I’ll return to how it does this, but first, how is an integrator started.

When IntegrationQueueManager is instantiated it iterates through all the projects and ensures that there is a queue for each project. If there is no queue, then it creates a new queue with the same name as the project. These queues are all added to an IntegrationQueueSet.

Once the queues have been initialised, the IntegrationQueueManager then calls ProjectIntegratorListFactory to generate all the project integrators. As well as containing the project configuration, the integrator also contains a reference to the associated queue. At the moment there is only one IProjectIntegratorProjectIntegrator.

This completes the initial setup of the queues and integrators, the next step is to start the projects integrating. This is done by calling the StartAllProjects() method (by CruiseServer), or by calling Start() for a specific project (StopAllProjects() and Stop() do the opposite).

When StartAllProjects() is called, it iterates through all the project integrators and checks to see whether the project can start. This involves checking the configuration and then the state persistence (both new in 1.4.3). If both these checks start, then the integrator is started. The actual starting of the integrator is done by calling the Start() method on the integrator.

Nice and simple, but here’s a diagram to illustrate this process:

Build Startup

Triggering Builds

The above initialisation got to the point of calling Start() on ProjectIntegrator. This method starts a new thread that contains a polling loop. This loop checks to see if there is an integration every 100ms. If there is an integration it then calls the Integrate() method on Project which performs the actual integration (e.g. pre-build, source control, tasks and publishers).

This check consists of two parts. First it checks the queue to see if there is a pending integration request. If there is a request, it locks any queues that need to be locked, starts a new request and calls the Integrate() method. After this it cleans up and exits the check logic.

The second part of the check, which is only performed if there is no pending request, is to check all the triggers. Each trigger has a Fire() method, which performs the actual check. The output of the fire method is an integration request or null – if the output is not null then it gets added to the queue.

The root level trigger is a combination trigger, which merely iterates through each child trigger and calls its Fire() method – it is up to each child trigger whether it returns a request or not.

Once the trigger checking has finished, it checks the queue to see if the request is next. If not, it enters a loop until the request is ready. Then when the request is ready, the check logic finishes – it doesn’t actually call Integrate() after the trigger checking. What happens is the polling loop goes through another cycle and then the integration request gets returned from the first part of the check.

The following diagram shows this:

Integration Polling

Queues

The final piece of the puzzle is the integration queues. I’ve already mentioned them a couple of times, but let’s pull them apart and see how they actually work.

First of all, queues do not use the built-in queue classes – they use a List<> instance instead. The reason for this very simple – they do more than just adding and removing items – they also allow re-ordering (based on priorities). Plus items on the queue are not removed until completed – which would cause issues with de-queuing.

In turns of how they work. When an item comes in, the queue checks to see if it already exists. If the item exists it applies any re-ordering rules (ignore, re-add or replace existing), otherwise it just adds it to the queue. It will look at any other items in the queue and then add it after the last item with the same priority, or before the next highest priority.

When the integrator checks for a request, it will always return the item in position 0 (the start of the list). This item remains there until the integrator performs its clean-up.

Finally, there are a few call-back methods that are used to synchronise the state between the queue and the integrator.

And that’s all there really is to queues – very, very simple. The following diagram shows how queues relate to the integration polling cycle.

Integration Polling Queues

Summary

This post has covered how project integrations work and how builds are scheduled.

The main driver for the process is IntegrationQueueManager, which responsible for initialising everything and then starting the actual integration cycle. The actual integration cycles are handled by a polling thread within each integrator.

The actual process for starting a build is controlled by both the integrator and the queue. The integrator checks the triggers to see if a build should be scheduled, and when one is found it adds it to its associated queue. Then, in the next polling cycle it retrieves the request off the queue and actually performs the integration.

The queues act to limit the number of project builds at any time, and do this by storing requests. The integrator will only perform a request when it is the first item in the queue.

Final Note

This post is about how builds are scheduled – I haven’t covered how an actual build is performed. But that is an entirely different topic, so I will leave that for another time :-)

Posted in CruiseControl.Net, Inner Workings | Tagged: , , | 3 Comments »

Playing with Themes

Posted by Craig Sutherland on 19 February, 2009

Previously…

Not that long ago I wrote a post on changing the appearance of the dashboard (read it here). In the post I included a number of pictures of a possible alternate appearance – and got some good feedback.

Additionally, Ruben suggested that we could add themes to the dashboard. Rather than forcing a user to go with one look and feel, we could actually give them a range of options and they could choose the one they like. Or they could even build their own!

I’ve been having a quick play around with this idea and I have some good news – not only is it very easy to do, but it’s also possible with the 1.4.3 build :-)

Some Examples

Since I had a little bit of spare time this afternoon I played around and put together a couple of themes. Here is what I came up with:

LiquidBlue

NoteWorthy

And just to give you an idea, here is the original:

Original

I certainly like my themes better – I think they add a bit of flair to the display and make it more interesting.

I should also mention that I’m not a graphics designer – first and foremost I’m a techie. These designs are the work of Designs by Darren (http://www.designsbydarren.com/). He has made a number of Creative Commons web templates available, and I modified two of them to work with CruiseControl.Net. I’ve very impressed with his designs and will probably convert a few more over. I’ll also check with Darren if he is ok for us to include his designs.

The How

The how of changing the appearance is now very easy. Ruben has included the ability to use templates from a location other than “templates”. This ability can be configured by setting a value in the dashboard.config file as follows:

<plugins>
  <customTemplates>themes/noteWorthy/templates</customTemplates>
  <!-- Remaining code omitted-->
</plugins>

This allows us to change the template to any location.

The other half of themes is to add one or more template files. At a minimum a new SiteTemplate needs to be added, and a new CSS file to contain the styles. I normally add ProjectGrid and TopMenu templates as well, as these allow me to customize a lot more of the display.

If Darren gives his permission, I’ll include the two themes I’ve already built as examples.

Limitations

At the moment my themes only really work for the grid displays. They are still applied to all the other pages, but I can’t guarantee they work completely. This is because there is no common set of styles across all the templates.

Additionally they are CSS-based, so any CSS limitations also apply (especially any limitations with “old” browsers).

Finally, should any of the templates change (e.g. in security) then the themes will break. This due to the lack of common styling, plus the requirement to modify the actual templates to achieve the styling.

Moving Forward

This was more of an experiment to test the concept of themes. It’s proved reasonably easy to do (with the above limitations).

For the moment I’m not going to do any more work on this, but maybe in the future I’ll look at standardising the styles across all the templates. This will simplify the development of themes and make them more resistant to change.

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

Inner Workings: Configuration in the Dashboard

Posted by Craig Sutherland on 18 February, 2009

Introducing Configuration

One of the nice things about the dashboard is the ability to customise the plug-ins that it uses. These plug-ins are configured in the dashboard.config file, which sits in the root folder of the web site.

While it is easy to configure plug-ins, the question is how can we modify how the code reads the configuration to include new configuration items?

First off, configuration in the dashboard uses NetReflector. This takes care of the actual serialisation and deserialisation of the configuration. In order to add a new property it is merely a matter of adding a ReflectorPropertyAttribute to the property that needs to be serialised/deserialised. The harder questions is where should this attribute be added?

Configuration Interfaces

A quick look into the configuration folder will show the following interfaces:

IW-Configuration Interfaces

These three interfaces define the information that can be retrieved from the configuration. The top interface – IDashboardConfiguration – defines the entry level configuration. This interface maps to the <dashboard> element in the configuration file.

The other two classes map to the next level in the configuration – IPluginConfiguration maps to plug ins, while IRemoteServicesConfiguration maps to <remoteServices>. Within each of these interfaces are the actual configuration properties, most of which map to other interfaces or classes.

The main point to know from here, is the dashboard stores these interfaces and uses them, instead of the actual implementations of the interfaces. The first place to add new configuration information is these interfaces.

Next, since these are interfaces, they don’t contain the NetReflector attributes – these attributes are defined on the actual implementations.

Configuration Implementations

There are four classes that implement the configuration interfaces:

IW-Configuration Classes

NetReflectorPluginConfiguration and NetReflecotRemoteServicesConfiguration are both very straight-forward. They implement the associated interfaces and contain the NetReflector attributes. There is a simple one-to-one mapping between the properties in the interfaces and the classes, and also to the configuration elements.

DashboardConfigurationLoader is the class responsible for loading the configuration settings. Internally it uses NetReflector to do the actual serialisation and deserialisation. It also implements IDashboardConfiguration and is responsible for exposing the second-level configuration, which is where things start to get a little messy.

DashboardConfigurationLoader uses lazy-loading for retrieving the configuration. When the instance is instantiated it calls GetTypeTable() to initialise NetReflector and then stops. Later, when the application wants to retrieve either the plug ins or remote services configuration it calls the relevant method (LoadPluginsConfiguration() or LoadRemoveServicesConfiguration()) to load the configuration and then stores it internally. These methods both rely on the Load() method to find the relevant XML node in the configuration and then use NetReflector to load it.

As such DashboardConfigurationLoader does not have any NetReflector attributes in it – instead it needs to be coded to handle new configuration items. As such it is easier to add new configuration items to either <plugins> or <remoteServices> than it is to add it to <dashboard>.

The final class in configuration is CachingDashboardConfigurationLoader. This class has a internal reference to a DashboardConfigurationLoader which it uses to do the actual work. The main work this class does is storing the DashboardConfigurationLoader instance in the HttpContext cache and retrieving it from there on subsequent use.

This means the first time the dashboard starts it will load the configuration from the file – every other time it retrieves the cached configuration. This is vitally important because it means the dashboard will not pick up any changes to the configuration! If the configuration is changed then the dashboard must be restarted!

Tying It Together – the Objection Store

While this explains how the configuration works, it doesn’t explain how to get an instance of configuration to where it is needed. The answer to this question is very easy – just add a IDashboardConfiguration, IPluginConfiguration or IRemoteServicesConfiguration argument to the constructor of the class that needs it. This argument will then be populated by Objection.

Objection is an IoC library that does all the bindings between the different components. It is smart enough to auto-detect which classes implement which interfaces, and it also has the ability to be configured. CruiseObjectSourceInitializer is the class that handles this configuration and it is used for linking the configuration interfaces to the actual implementations.

For configuration, all three interfaces are loaded into the store. CachingDashboardConfigurationLoader is loaded for IDashboardConfiguration and the other interfaces are loaded from the properties on CachingDashboardConfigurationLoader.

Finally, the good news is CruiseObjectSourceInitializer is called automatically by the system prior to any other processing. This means it is then available to every other class.

Summary

Configuration in the dashboard is exposed via three interfaces: IDashboardConfiguration, IPluginConfiguration or IRemoteServicesConfiguration. IPluginConfiguration and IRemoteServicesConfiguration use simple NetReflector-based implementations for exposing configuration, while IDashboardConfiguration is implemented by a loader class.

There are two configuration loader classes – one that does the actual load and a second that caches the results. This caching loader means any configuration changes will not be detected until the dashboard is restarted.

Finally configuration is made available by requesting it in the constructor of a class. Objection is responsible for adding the configuration implementation and can do so because it has been loaded in CruiseObjectSourceInitializer before any other processing is done.

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

Inner Workings Posts

Posted by Craig Sutherland on 18 February, 2009

Since I’ve been involved in coding for CruiseControl.Net I’ve spent a lot of time just trying to figure out how it works. Some things are straight-forward and can be changed with a minimum of fuss. Other things seem to obfuscated and are almost impossible to change.

However, given enough time – both to read the code and debug it – it’s possible to understand what is happening. And rather than forget all these things I’ve spent the time learning, I thought I’d write them down.

These posts will be technical and aim to cover different hard to understand parts of CruiseControl.Net. I’m going to try and cover all the components of CruiseControl.Net – server, CCTray and dashboard – with a focus on some of the more challenging areas.

As a background I’m going to assume people know C# and have a basic understanding of the different parts of CruiseControl.Net (Remote, Core, WebDashboard, CCTrayLib, etc.) plus have looked around the code some.

Mainly I’m going to focus on how these issues affect development. They will be built around breaking down a “problem” area and seeing how to work with the code. As such I’m more interested in the code rather than the functionality, although the two are related. So what I cover will have relevant to how CruiseControl.Net works, but the focus won’t be on it.

So stay tuned and feel free to give me any feedback – I promise I’ll listen :-)

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

Sprucing Up the Dashboard

Posted by Craig Sutherland on 17 February, 2009

A while ago I wrote a post on different ways we can change the appearance of the dashboard (read it here). One of the ways I demonstrated in the post was how the CSS and templates could be modified to allow different appearances.

At the time I wrote the post because I was more interested in the possibilities of what we could do with the dashboard. However, there is another side to the dashboard – the styling looks outdated. I’ve been talking to a few other people about the dashboard and everyone I’ve talked to think it would be nice to give the dashboard a make-over.

So I’ve put together a few screen shots of what it could look like:

Entry

Server List

Logged In

Invalid Permission

BuildReport

These were all achieved by modifying the NVelocity templates and changing the main style sheet.

What do you think?

PS: Since I originally did these screen-shots I have had a suggestion that we could add multiple themes and let people choose which theme they want to display. Would the ability to make and use themes for CruiseControl.Net be useful?

Posted in CruiseControl.Net | Tagged: | 6 Comments »