Archive

Archive for January, 2011

CruiseControl.NET – vNext – Controlling the Flow

28 January, 2011 3 comments

Over the past day or two there have been a couple of interesting scenarios raised about the “flow” within CruiseControl.NET. At the moment I don’t have an answer for these issues so this post is more about raising the visibility so they don’t get forgotten.

The first issue is about interrupting tasks. While we are making some changes to tasks to allow for breaking the flow of a task these changes are more for tasks that are containers of other tasks. However another scenario was raised recently where we might want to break the internal flow of a task. By this I mean we want to stop a task from running during its normal process. This was raised in response to the parallel task – where there are two tasks that take a long time. If one task fails then there is no point in continuing the other task – instead it should be cancelled so the entire build is failed more quickly.

This however raises the issue of forced vs. co-operative cancellation. The preferred approach is co-operative cancellation. Both the task and its parent share some mechanism for cancellation. When the parent cancels the task it stops at the first convenient opportunity. This means it can ensure that everything is reset to a proper tidy state and there is no corruption of any data. The other approach – forced cancellation – is where the parent just kills the task. This means the cancellation is immediate – no need to wait around – but it also means that data can be corrupted. In other words we have absolutely no idea of what has happened!

Now we could expand the mechanism that is already in place for vNext to handle returning non-tasks (perhaps null) and then let the parent container handle the cancellation. But the current mechanism is specifically for running tasks so it would mean making the process more complex and it still wouldn’t allow for the task to perform any tidying up. An alternate is to add a new mechanism for cancelling the task and then getting the task itself to monitor this mechanism (similar to how cancellation works in .NET 4.0.)

Something to think about – I’m open for any suggestions on what people think will be the best way.

The second issue is around additional control for the meta-structures (queues, gates, etc.) Currently there are four meta-structures we are planning on adding:

  • Queues: Allow only one (or more) active integrations at a time – take the next integration either LIFO or FIFO
  • Round-robin: As a queue but using round-robin scheduling
  • Gate: Waits for all active integrations to complete before allowing the next item to continue – this has a variation that will trigger all the children when the gate is hit
  • Pipeline: Only allow the children to trigger within the order they are within the pipeline – one variation will wait for external triggers, the other will trigger each item in sequence

The additional control that was requested is for a “clean-up” integration to start when a queue has finished (i.e. there are no active integrations.) There are two ways I can think of doing this at the moment. One is we add a new control structure (e.g. GatedQueue) that remains active until the queue is clear. This could then be combined with a pipeline to trigger the “clean-up” integration. The second is we add start-up/clean-up properties to the queue that contains projects that must be run before/after the queue. At the moment I prefer the first approach as it is a bit tidier.

Does anyone else have any suggestions or preferences?

UI Goals: Patrick

Continuing with the UI design we come to Patrick’s goals. As I stated in the personas (read them here) I’m not really sure that Patrick is a primary persona but as he is probably going to need a separate interface for his work I’m treat him as a primary persona with a complete set of goals.

Simple and easy to use

Since working with CruiseControl.NET is only a small part of Patrick’s job he does not want to have to spend a lot of time learning the system. The interface should be intuitive to use, allow him fix any mistakes without them becoming serious problems and only require a minimum of work. He should also be able to setup “templates” for different projects that can be quickly applied.

Configure the entire system

Patrick should be able to configure everything in the system from a single console. He should not need to swap between different programs to set things up. This includes setting up, configuring and upgrading multiple different build servers. The “everything” that he needs to configure includes the different build projects, users and their security access, servers, etc.

View complete diagnostic information on the entire system

When Patrick is trouble shooting he should be able to see exactly what has happened if anything fails. When an integration fails he needs to know which project failed, which step in the integration and why it failed. Since he is only called in for fixing the problems that developers can’t (since he growls at them if they bug him) he needs to see more than just want the developers see. This information includes details about the servers (memory, CPU usage, available disk space), any other processes that were running (and might have caused interference), etc. Of course the information should be in a format that makes it easy for him to spot the cause of the problem quickly!

Automated monitoring

Given that Patrick prefers not to be bothered by developers he would much rather be notified of problems by the system directly rather than the developers. This means he can fix things before they come to him, which in turn makes the developers even more impressed with his abilities. As such he wants the system to notify him when certain parameters are exceeded. As he already monitors other systems and servers for Rainbow Interactions he wants the notifications for CruiseControl.NET to appear in his monitoring tool rather than needing a separate tool.


Comparing Patrick’s goals with Michelle’s we see that they both have a different focus. Patrick prefers an in-and-out approach – get in, do his tasks and get out – as quickly as possible. He also does not want to become an expert in the system so it needs to be as easy to use as possible. In contrast Michelle is more of a regular user. She is happy to spend some time to become familiar with the system if it will make things easier for her in the long run. They also both have some common features: it should work within their current tools, it should notify them when things go wrong and it should provide them access to everything they need in a single location (although for Michelle this will be two different locations.)

Hopefully this is starting to sound like an interesting challenge Smile

Next step, time to put together some context scenarios to provide some generic detail on when and how they will approach their different tasks. Stay tuned…

UI Goals: Michelle

In my last post I described a couple of personas for designing the UI/UX for a new CruiseControl.NET (read it here.) In this post I’ll take these personas and outline some goals for them.

For both of the personas we need an experience goal (what sort of experience they expect to have when using the system) and some end goals (what they are trying to achieve.) These are not technical goals instead they need to be aimed them at a higher level – what is the user trying to achieve. This gives us some more flexibility around how we meet the goals (i.e. the goal does not describe how it can be achieved.) In this post I will cover the goals for Michelle:

Flows with her working style

Michelle has two primary ways to access the build information: from within Visual Studio and over the web. She should be able to see the same information and make the same changes from both Visual Studio and the web. All build information and artefacts need to be accessible.

Displays all the information to diagnose problems with a build

When a build goes wrong Michelle needs to be able to quickly and easily see where things went wrong and why. To start with she needs to see that there is something wrong with a build. From here she should be able to drill down to find the exact place that the build failed and why. She would also like to see who added the code that failed the build and when.

Control a build project

As the lead developer for Here and Now Shipping’s system Michelle needs to have full control over how the build project works. The main action she needs to do is to start a new build integration, but she also needs to occasionally abort builds, start and stop the process, etc. A task she performs on a much lesser basis is modifying the configuration for a build.

Be notified when something goes wrong

If something goes wrong Michelle needs to be notified as soon as possible. She does not want to have to wait until she manually checks to find out something is wrong. The notification needs to have sufficient detail for her to know where to start looking for the problem.


As you can see these are all very high-level. They describe what the user (in this case Michelle) wants to do in the system without providing the implementation details. The next step in the process is to put together some scenarios that show when Michelle would use the system.

So stay tuned, more information is coming…

UI Personas

21 January, 2011 1 comment

An important part of designing an user interface (UI) or user experience (UX) is to know who we are designing for. One of the tools for this in interaction design is to develop some personas. These are fictional people who embody all the goals of actual users of the system. So for re-designing the UI for CruiseControl.NET I have put some personas together.

Note: these are not true personas as they are not based on research. Instead they are based on my understanding of a typical user; so they will not be as effective as true personas.


Patrick

37 year old male

Patrick is the systems administrator in a team of 12 at a small software development house called Rainbow Interactions. His job involves implementing and improving processes within the team. He directly reports to the CEO of Rainbow Interactions and often works directly with their customers. As such he has a very busy schedule and likes his time to be as streamlined as possible.

Outside of work Patrick is married with three children. As he spends a lot of time at work during the week he refuses to work during the week-ends so he can spend time with his children. He enjoys walking in the wilderness and so will often go away during the week-ends; which also means that work cannot contact him with any “emergencies”.

Patrick is involved in the initial setup of every project and then helps the other developers as required. He is also involved in cleaning up after each project has completed. His work varies from day to day depending on where in the project cycle the team is at. At the beginning of the project he makes time for setting up everything required for the project, including continuous integration (CI). During the project he will help the developers trouble shoot problems and even occasionally write code. Finally at the end of the project he archives all the work for the client. He might also go to the client’s site to help transfer the work over if necessary.

As part of their process for working on client projects they always set up a new CI process. Each process has a common core with the basic functionality (e.g. retrieve latest changes, compile the code, etc.) but they can all be customised depending on the clients’ needs. While Patrick sees this as an important part of his job he doesn’t have much time that he can spend on setting up CI processes. Instead he just comes in, copies the default template and then adds in the modifications as required. As different developers can be working on different projects, and sometimes the clients also have access, he likes to lock down the processes so only the relevant people can have access.

Patrick only helps with trouble shooting if there is a problem that the developers cannot solve themselves. His first question when they come to him for help is to ask what they have already done. If they have not tried anything (e.g. don’t know where to start) or they have missed something then he will advise them where to look. If it is a problem outside of their ability or capacities then he will get involved. These are normally problems on the CI servers (maybe bad configuration, maybe server issues, etc.) The first place he always looks is the server logs and from here he will trace the problem. As such he likes to see as much information as possible (which might include elevating the logging level to get additional information.)

To archive a project all he does is select the project and the destination and then starts the process. This copies everything to the destination and removes the process from the server. Later on he can restore the process or he can install it on another CI server if needed.


Michelle

26 year old female

Michelle is a developer at Rainbow Interactions. She has been working on a project for their most important client – Here and How Shipping – for the past five years and has recently become the lead developer for their system. Since Here and Now Shipping has been with Rainbow Interactions for several years now they have a well-defined CI process. However from time to time things change and the process needs some improvements (for example they learnt about code coverage recently and wanted that included.)

Michelle’s day-to-day job is very simple – she takes the list of requirements she gets given from the business analyst at Here and Now Shipping and works with her time to implement them as quickly as possible. Most days this involves writing code, sometimes testing, sometimes bug fixing. No matter what she is doing though she is always running short of time and will often stay late at work, with the occasional week-end day as well.

She just expects the CI process to work – when she (or any of the other developers) checks in code for Here and Now Shipping’s system it should automatically build and then notify her of any problems. If the build goes fine she doesn’t care (although sometimes she might look at it to just touch base” but it breaks she wants to know as soon as possible. When the build does break she wants as much information on why it has broken as possible so she can either fix things or chase down the person who broke it. The last thing she wants to do is bother Patrick as he is often grumpy if developers bother him without a “good” reason.

Her primary development environment is Visual Studio, but she also accesses build information remotely via the internet. She likes to see all the information possible on the build (number of changes, which unit tests were run and how long it took, the amount of coverage, any coding rule exceptions, etc.) She also needs to start and stop the process, trigger builds and modify the configuration if necessary. Basically she needs to do everything with the CI process for Here and Now Shipping. But conversely she doesn’t care about any of the other clients Rainbow Interactions works with.


Of these two personas I’d put Michelle as the primary persona as she is the main user of the system. However Patrick would have a different set of interaction goals so he would probably be another primary, although not as important as Michelle.

In my next post I’ll look at some interaction goals these two personas have and maybe some scenarios. With these in place we can start to look at how we can design the interaction process for our users. Open-mouthed smile

Stay tuned…

The UI of CruiseControl.NET: Introduction

19 January, 2011 2 comments

I’m going to take a break from looking at the inner workings of CruiseControl.NET vNext for a while. Instead I’m going to spend some time posting about another important area that doesn’t get as much attention – the User Interface (UI).

There are some areas in CruiseControl.NET (CC.Net) that tend to turn people off. I often see people mentioning they use alternate products because of “blah”. Now these are not necessarily UI problems but a good UI can go a long way to helping people enjoy a product despite other problems (just look at Apple products as a prime example!) So any love and attention we put into the UI will pay off a 100-fold.

The following diagram shows the current components and their interactions in CC.NET:

image

The server is a black box to the user. Yes there is a console version that will display the log but the user has no way of interacting within the server beyond just seeing what is happening.

Instead the user needs to use an external component to interact with the server. For example the web dashboard is a web-based application that allows a user to see what is happening on the server and to make some limited changes (e.g. force/abort a build, etc.) The grey lines show the direct communications with the server – which is via .NET Remoting.

CCTray is the second type of interface application. It is a Windows-based client that also allows viewing the server status plus some changes. Unlike the dashboard it can use HTTP-based communications but this is an indirect path – it needs to go via a dashboard to actually connect to a server. And CCTray tends to be a lot more limited in the functionality it offers.

There is a third path of interaction – via the ccnet.config. This is the path that the validator takes. It does not directly interact with the server at all, instead it reads the same configuration. And of course people directly modify ccnet.config as the ultimate way of controlling the server.

There are also a few more applications that can be used to interact with the server: cradiator (BVC), FastForward.NET, CCCmd, etc. These all tend to fit in the same space as CCTray in that they use either HTTP or .NET remoting to connect to the server (although some only use HTTP.)

Each of the various UI components has their own limitations. The dashboard is reasonably easy to configure and extend (if you know how it fits together) but it is global in scope. There is no way to configure it to have custom views for each user. In contrast CCTray is easy to personalise but the options for extending it are very limited (especially as there is no plug-in framework for it.) Plus going via the dashboard for HTTP means it can be slow. Finally (and this is one of the main complaints I see) there is no way to easily modify the configuration. The config file is XML – if you want to configure CC.Net you have to use XML. End of story!

So going forward I’m going to look at how we can apply some interaction design principals to CC.Net to improve the user experience. If there is anything that annoys you about the current UI or any suggestions you have let me know and I will see if (and how) they can be resolved or added.

CruiseControl.NET – vNext – TaskExecutionContext (cont.)

In my last post I looked at how the new TaskExecutionContext works at a high level. In this post I’ll describe the current methods on the context and where they fit into the process.

When an integration is started a new TaskExecutionContext is created using StartNew() on ITaskExecutionFactory. This creates a new folder for the integration, starts an XmlWriter for the build log and adds the basic project details to the log.

The next call in the sequence is to StartChild() on TaskExecutionContext. This creates a new child instance of the context that is specific to a task. This will output the task details (e.g. name and type) to the log. An instance is created for each task by the Project – this then gets passed into the task.

Once a task has finished the Complete() method is called. This writes the final details of the task to the log and tidies up. The same complete method is called whether the context is for a task or for an integration. The only difference is the integration level call will close the log at the same time.

There are currently four methods that can be called from within a task:

  • AddEntryToBuildLog(): adds a comment directly to the log
  • ImportFile(): copies/moves a file to within the folder for the integration and adds an index entry to the log
  • StartOutputStream(): creates a new file within the integration folder and adds an index entry to the log
  • AddModifications(): adds any modifications from a source control block to the context

There are also a number of properties that are available. These are:

  • CurrentStatus: the current status of a task. This can be set by a task but if not set then calling Complete() will set it to Success.
  • IsCompleted: a read-only property flag indicting whether a context has been completed. Calling any methods on a completed context will throw an exception.
  • Request: the original integration request details (read-only).
  • Project: the project the integration is for (read-only).
  • Parent: the parent context. If the context is the project level context this will be null. Otherwise this will point to the context that created the current context (i.e. the context that StartChild() was called on)(read-only).
  • ModificationSets: the current modifications within the integration (read-only).

One point to note about ModificationSets is there is only a single instance for an integration. This is why AddModifications() is required – it handles any locking required so they are no thread conflicts when adding or retrieving modifications.

So despite the changes to how the context works, from a task authoring perspective it should be even simpler to use.

CruiseControl.NET – vNext – TaskExecutionContext

14 January, 2011 3 comments

One of the important areas in vNext is the TaskExecutionContext. This is both taking over from IIntegrationResult in the current versions and providing additional functionality.

One of the major differences between TaskExecutionContext and IIntegrationResult is there will be a single TaskExecutionContext per task. In the current versions there is only a single IIntegrationResult for an integration – it is up to the tasks themselves to correctly manage the properties on the instance. As you can imagine this can potentially led to issues where the properties are treated differently between the different tasks. So in vNext each task will have its own TaskExecutionContext instance. The controller (typically the project) is responsible for starting these instances and completing them once the task has completed.

This important difference also affects how the contexts are started. The initial context (at the project level) is started via a factory class (TaskExecutionFactory). This will initialise all the required settings for the instance. Subsequence contexts are then started via the StartNew() method on the current context. This generates a new context that is associated with a parent – which means the log entries will be written to the same log.

This is important because of a second major change – log entries are now written directly to a file instead of being stored in memory. This is to get around the nasty OutOfMemory problem that we are having with the current versions. As such this significantly changes the way that the build log is being written. In the current versions the log is written by (and only by) an xmlLogger task. If this task is omitted or fails to execute then there will be no log. As you can imagine this can be slightly disconcerting for new users (I remember it was for me!) Now because the log is emitted straight to disk it will always be written – which makes the xmlLogger task obsolete.

With this in mind, the format of the file has changed significantly. I am still working on the format and re-adding all of the goodness that’s in the current versions, but here is an example of what the file is shaping up as:

<?xml version="1.0" encoding="utf-8"?>
<project name="TestProject">
  <start>2011-01-09T15:28:43</start>
  <task type="Comment">
    <start>2011-01-09T15:28:44</start>
    <entry time="2011-01-09T15:28:44">A Test Comment</entry>
    <finish>2011-01-09T15:28:44</finish>
    <status>Success</status>
  </task>
  <task type="MergeFiles" Name="NUnitFiles">
    <start>2011-01-09T15:28:44</start>
    <file time="2011-01-09T15:28:44">testproject.xml</file>
    <file time="2011-01-09T15:28:52">otherproject.xml</file>
    <finish>2011-01-09T15:28:53</finish>
    <status>Success</status>
  </task>
  <finish>2011-01-09T15:28:53</finish>
  <status>Success</status>
</project>

This now contains a wealth of information on the actual tasks that were executed. Details for each task appears within the tasks themselves (this will include references to imported files, embedded data, etc.) We can quickly and easily see:

  • The name (if any) and type of a task
  • The start and finish times
  • The final status
  • What data was added during the task

I’ll also flesh out the project data to include most of the data that is in the current build log.

Finally the other significant change I have had is external data is no longer directly embedded within the log. This was something that worked when we played around with fixing the out-of-memory issue (unfortunately it never got implemented) so we will include it in vNext. Instead of directly embedding the data within the file we add a reference to the file instead.

The advantages of this approach is our build logs will be a lot smaller as it now contains the build summary instead of all the details. It also gets rid of the problems around XML encoding –currently if the source file was invalid XML (or not XML at all) it would be put in a CDATA section. And since the files are linked to a task it is easy to see which task generated the file – especially if there are duplicates of the task type within the project.

The main disadvantage to this is the client will need to first parse the build log to get the names of the relevant files and then get the relevant files from the server. This could be troublesome when generating the build summary but given that the build summary is static we could look at some way of either caching this on the clients or pre-generating the summary on the server.

So this is a look at some of the differences between the current and new build logs, in my next post I’ll look at some of the nuts and bolts of how the log is generated.

CruiseControl.NET – vNext – Invokable Methods

12 January, 2011 3 comments

Now that we have a naming standard for locating any item within the configuration and a standard API for querying and invoking the available actions the next question is how do we expose new actions? As stated earlier I want this to be as easy as possible for people to add new actions (unlike the current state that requires modifying multiple locations!)

To achieve this there are two parts that need to be done.

First to pass arguments and return values a sub-class of BaseMessage is needed. There will be a few default sub-classes that will hopefully provide most of the required functionality but it will also be possible to add new sub-classes as needed. These classes will allow passing around the information between the client and the server plus a few extra bits and pieces of plumbing (e.g. security information.)

The second part is to implement the method and add a RemoteActionAttribute to its definition. This method needs to take in a sub-class of BaseMessage as the argument and return a sub-class (it can be the same but it can also be different as required.)

And that’s it – hopefully nice and simple Smile

There is one additional step – add an optional DescriptionAttribute. This will be used by the framework as a human-readable description that gets passed around.

For example for a ForceBuild action we could have the following definition:

[RemoteAction]
[Description("Trigger a build remotely.")]
public BuildMessage ForceBuild(ProjectMessage request)
{
    // Implementation omitted
}

Actions can be exposed on any item within the configuration (e.g. server, queue, project, tasks, triggers, etc.) The invoker will use the universal name to find the correct object and then either query or invoke the actions. The List method will check for all methods that have the RemoteActionAttribute and return the name and description (if present.) The Query method will add the input and output message types. And finally the Invoke method actually calls the method passing in the arguments and returning the result. Under the hood this uses Reflection to do all the dirty work – but that is the subject of another post.

So up to this posts I’ve covered the basics of the communications. The next question (and my next post) is how does this get exposed to the client? I’m looking at using WCF for the default implementation but it will be generic enough to cover additional protocols as required. So stay tuned and in my next post I’ll cover the implementation of a communications channel on the client.

CruiseControl.NET – vNext – Three External Methods

10 January, 2011 5 comments

Another area that is currently challenging to work with is exposing new functionality to external clients. On the server-side along there are three classes and an interface that need to be modified alone just to expose the functionality, let alone actually adding the functionality or modifying the clients. In addition modifying the interface causes other parts of the code to break (mainly in the unit tests) so it is something that I usually try to avoid.

To simplify things in vNext I’d like to only expose three methods – these methods would then expose any additional functionality we want without having to modify the actual communications infrastructure. We already have a version of this with the messaging infrastructure that was added in 1.5 but I’d like to take this a step further and also handle some scenarios that the messaging does not handle.

So in a nutshell the three methods I am proposing are:

  • List
  • Query
  • Invoke

These three methods provides the basics of a communications framework where any item in a server can expose functionality. I’ll come back to this in a minute but first let’s go over these three methods.

The List method will list the possible actions that are possible for an item. The method has only one parameter – a universal name of an item. The communications framework will look up the item based on its name, retrieve its type and then get the possible actions that are available for the current user. For example a project type item might allow GetStatus, ForceBuild, AbortBuild, etc. actions while a queue type item might have GetPendingRequests, CancelRequest, etc. This method will return a list of actions – each action will have a name and a description.

The Query method will provide the details on an action. Like the List method it requires a universal name and it also requires the name of an action. If the action is available and allowed it will return the description and the input and output parameters. If the action is either unavailable or not allowed for the current user it will return nothing.

Finally the Invoke method is what is used to call an action. It requires the universal name of an item, the name of an action and its required input parameters. It will invoke the action and then return whatever output parameters are generated. If the action is not available or not allowed then an error will be generated and returned.

These three methods provide a way for external clients to not only run actions on the server but to also determine whether an action is available. For example force build in 1.5 or later versions can be locked down with security but the dashboard has no way of knowing this. The only way a user can find out if they are allowed to force build a project is by clicking on ForceBuild which will generate an error in return – not very nice!

With the List and Query methods the client can check to see if a user has access to the action, maybe as part of the UI generation or as a sanity check before invoking the action. The description can also be used to return help information to the user as required. It will also be possible in future to generate dynamic interfaces where the list of available actions is dynamically generated based on what the server returns, the user selects the action and is prompted for the input arguments and then performs the invoke.

The other part to having these three methods is any item on the server can expose methods – not like the current state where it is primarily projects that have methods with a few additional methods floating around. Using the universal names we can list the possible actions on any item and invoke them. For example there are some items within a project that store state: e.g. the Subversion source control block can store the last revision number. We could add an action to this block to retrieve this state and return it to the user and we could add another action to allow the user to update the state. By having it on the level of the item only the item needs to know about the actions (we don’t need to keep bloating Project with actions for child items.)

Now I have covered two pieces of the communications framework I’m wanting to implement – universal names and the public methods. The third part is how to tie these together – how can we expose actions on various items and have them picked up by the communication structure. As you can imagine this is the heart of the new structure so I will cover it in my next couple of posts.

Stay tuned…

CruiseControl.NET – vNext – Universal Naming

8 January, 2011 4 comments

Another area that is challenging in the current version of CruiseControl.NET is communications. To add a new message that will be used in communicating with a server requires modifications in multiple places – both on the server and the client (although some of the changes made in the past couple of years has made this easier on the client side.)

In addition CruiseControl.NET only supports one protocol for talking to the server – .NET Remoting. There is a web-based protocol but this requires an installation of the dashboard which acts as an intermediate. And as the bug reports show this has caused some other problems. There is an WCF extension that can be used but because extensions were tacked on later not many people know about them – especially as they are configured via the app.config instead of ccnet.config!

For vNext I’d like to take this another step further and abstract away more of the communications infrastructure. This will allow for additional communications protocols natively on the server. Plus we can handle from the start some of the issues that have been raised in the current web-protocol.

These changes will involve a number of different components which I’m planning on covering in the next few posts. In this post I’m going to start on a simple that allows a lot of functionality: universal naming.

Currently we have a bit of a mixed bag with the current versions – we can access some items remotely; others are more hidden. For example it is very easy to access and work with projects – the ever present project name servers as a very useful identifier. It is possible to get some information about queues and tasks but normally this is accessed via a higher level item (e.g. the server snapshot or via a project.) If I wanted to find out how an MSBuild task on a project is configured I would have to get the whole project’s configuration and then extract just the task I wanted – not very obvious.

What I would like to propose is a universal naming standard that allows almost any item within the server to be addressed. This will make it different from how things currently work but it will allow much more control over what is accessed.

The base of this naming is to use non-standard URNs. These will be non-standard in they will not use an officially defined namespace id but otherwise will work just like a standard URN. The namespace will be ccnet – so all identifiers will begin “urn:ccnet:”. As per RFC 2141 “urn” is case-insensitive and “ccnet” will be also.

The generic form of a ccnet urn is:

urn:ccnet:server:path-to-item:item

server and item will always be required. server is either the unique name of a server or its address. For example for the server ccnetlive located at http://ccnetlive.thoughtworks.com server would be either http://ccnetlive.thoughtworks.com or ccnetlive. If ccnetlive is used then the mapping to the URL will need to be stored somewhere else (more on this in a later post.)

item is the unique name of an item. Which item depends on the path that has been entered. To help with resolving an item identifier I think we need a few rules around names within the configuration:

  1. All item names within a level of configuration must be unique
  2. All projects must have unique names that cannot be the same as another root-level item
  3. All project items must have either a single instance or a unique name

Rule #1 basically means that at each level the name will identify a unique item. For example a level might be the root level of the server configuration or it might be the child items within a queue, tasks within a project, etc.

Rule #2 means we have an easy way to identify requests for projects (more on this in a minute.) Ensuring that project names do not exist at the root (unless the project is at the root) removes any confusion between referring to a project or a root level item.

Finally rule #3 means we can refer to individual items within a project. The single instance part means if an item is not named then there cannot be any other instances of that item type. For example if there is an unnamed MSBuild task then there cannot be any other MSBuild tasks within that project or project item.

Returning back to the URNs if we had a project called ccnet-trunk on ccnetlive the basic URN would be:

urn:ccnet:ccnetlive:ccnet-trunk

The final part of the URN is the most interesting part – a path to an item. Coming back to our earlier example of an MSBuild task within a project we could reference this task by using the project name as the path:

urn:ccnet:ccnetlive:ccnet-trunk:msbuild

In this example the path is “ccnet-trunk”. The power of this comes when we start looking beyond just the simple project/task structures. For example if we wanted to look at a queue within a queue we could have something like:

urn:ccnet:server:outerqueue:innerqueue

Here the item is innerqueue and the path to it is outerqueue. This means only projects need unique names – other structural items can have duplicate names as long as they are not duplicated under an item – we could have another item called innerqueue that exists under an item called roundrobin for example.

We could also use this syntax for looking at items that belong to other items. If we had an MSBuild task within a Sequence we could use:

urn:ccnet:ccnetlive:ccnet-trunk:sequence:msbuild

This syntax would allow us to access any part of a server’s configuration easily and consistently.

There is one additional area that needs addressing and that is for individual builds. For example we might want to retrieve the status of a build of a project. Now this by itself has a couple of issues: first the build is more of a filter rather than an actual part of the address and second there is currently no easy way to address every build. Projects do have labellers that generate unique labels for a build, but these are only guaranteed unique for successful builds (i.e. the labels can be re-used from failed builds.) To get around this the current version uses a combination of a date/time stamp, the build status (passed/failed) and the label for uniquely identifying a build!

So to address the first issue I think we should use a different symbol for identifying the build, and I am proposing “->”, this would be appended to the end of the URN. For the second issue I think we should use either the date/time stamp or the build label for the name of a build.

For example if there was a build for the above project that started at 13:02:14 on 12-Dec-2010 we could use the following URN for the build:

urn:ccnet:ccnetlive:ccnet-trunk->20101212130214

The date/time stamp should only go to the nearest second (otherwise it gets too long) and we should enforce that a project cannot start building more than once a second.

Alternately if the build was successful and generated a final build label of 2.0.1 then we could use:

urn:ccnet:ccnetlive:ccnet-trunk->2.0.1

This would also be usable with tasks and other project items:

urn:ccnet:ccnetlive:ccnet-trunk:msbuild->2.0.1

Using the alternate symbol (->) instead makes it easier for address resolution. If we used colons for both the build and the item name when we would need to do a look-up to decide whether it is a build name or otherwise.

So this post has covered a naming standard we can use for addressing different parts of a server. As a preview there are two additional parts I’m planning on covering for communications:

  • The Three External Methods: looking at how we can condense our external surface to use three methods (List, Query and Invoke)
  • Invokable Methods: looking at how we can expose any method on any item so it can be invoked by an external client

So if you don’t know why I want to add universal naming to vNext stay tuned and (hopefully) things will become clearer.

And as always, I’m happy for feedback, so let me know what you think.

Follow

Get every new post delivered to your Inbox.