My Blahg

August 13, 2008

CruiseControl.NET and multiple build configurations: Part 7 - Committing changes

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 12:05 pm

[More Posts On This Topic]

This is post 7 in a series devoted to extending CruiseControl.NET to support multiple build configurations for a single project. In this post, I will revisit the dashboard plugin and discuss how the property changes are committed. This will be a lengthy post.

Where We’ve Been
Before I get into the meat of the post, I’d like to recap what I’ve covered so far. The idea is to create some user interface element in the dashboard from which the user can modify nant script properties and have them used by the nant script at build time. I’ve chosen to implement this as a dashboard plugin with a corresponding server side plugin. The dashboard plugin provides the user interface for editing the nant property values, and the server-side plugin provides the functionality of passing these property values to the nant script at build time.

Back to the Dashboard Plugin
The last post covered the server side plugin. It’s complete, and doesn’t need to be revisited. The remaining work all takes place within the dashboard plugin. The fifth post in this series introduced the implementation of the Dashboard plugin, but left it incomplete. I’ll correct that shortly.

Reference: Dashboard Plugin Source
Before I get into the details of the remaining implementation of the plugin, I’ll post the source. You can review it now, or come back later:


/// <summary>
/// This class implements the custom dashboard plugin that will allow the user
/// to modify the project's nant script properties via the browser.
///
/// This plugin is a project plugin, and appears as a link via the project page.
/// </summary>
[ReflectorType( "propertiesPlugin" )]
public class PropertiesPlugin : ICruiseAction , IPlugin
{
	/// <summary>
	/// The xpath to the params task
	/// </summary>
	private const string XPATH_PARAMS = "project/tasks/parameterizedNant";

	/// <summary>
	/// The name of the plugin
	/// </summary>
	private const string PLUGIN_DESCRIPTION = "NAnt Project Properties";

	/// <summary>
	/// The name of the action
	/// </summary>
	private const string ACTION_NAME = "ViewProjectProperties";

	/// <summary>
	/// Generates the html view used by the dashboard; injected into the constructor
	/// by the runtime
	/// </summary>
	private readonly IVelocityViewGenerator _viewGenerator;

	/// <summary>
	/// Reference back to the actual ccnet server.
	/// </summary>
	private readonly ICruiseManagerWrapper _manager;

	/// <summary>
	/// Initializes the plugin. The arguments are set by the dashboard/netreflector runtime
	/// </summary>
	public PropertiesPlugin( IVelocityViewGenerator viewGenerator , ICruiseManagerWrapper manager )
	{
		_viewGenerator = viewGenerator;
		_manager = manager;
	}	//END constructor 

	#region ICruiseAction Members

	/// <summary>
	/// Executes the request
	/// </summary>
	public IResponse Execute( ICruiseRequest cruiseRequest )
	{
		// get the request and the current project
		IRequest req = cruiseRequest.Request;

		// read all of the properties as they are currently defined in the project's config
		List<PropertyItem> existingProperties = ReadItems( cruiseRequest.ProjectSpecifier );

		// determine if we are displaying the properties, or committing the properties
		if( IsRefresh( req ) )
			return Commit( existingProperties , cruiseRequest );
		else
			return ExecuteNew( existingProperties , cruiseRequest );
	}	//END Execute method 

	#endregion

	/// <summary>
	/// Determines if the current page view is a normal view, or if the form has
	/// been submitted as a postback(and subsequently the modified property values will
	/// need to be committed back to the ccnet server).
	/// </summary>
	private bool IsRefresh( IRequest request )
	{
		string key = request.FindParameterStartingWith( "Refresh" );
		return key != string.Empty;
	}	//END IsRefresh method 

	/// <summary>
	/// Reads all of the properties from the project's configuration
	/// </summary>
	private List<PropertyItem> ReadItems( IProjectSpecifier projectSpec )
	{
		// read the project configuration, in xml form
		string config = _manager.GetProject( projectSpec );

		// load the config into an xml document
		XmlDocument doc = new XmlDocument();
		doc.LoadXml( config );

		// find the parameterized task node. If no node is defined, then return
		// an empty list of property items
		XmlElement node = ( XmlElement ) doc.SelectSingleNode( XPATH_PARAMS );
		if( node == null )
			return new List<PropertyItem>();

		// deserialize the custom server plugin from the xml node
		NetReflectorTypeTable typeTable = NetReflectorTypeTable.CreateDefault();
		typeTable.Add( typeof( PropertyItem ) );
		typeTable.Add( typeof( ParameterizedNAntTask ) );
		ParameterizedNAntTask task = ( ParameterizedNAntTask ) NetReflector.Read( node , typeTable );

		return new List<PropertyItem>( task.Properties );
	}	//END ReadItems method 

	/// <summary>
	/// Updates the configuration
	/// </summary>
	private void UpdateConfiguration( IProjectSpecifier projectSpec , List<PropertyItem> properties )
	{
		string config = _manager.GetProject( projectSpec );

		// load the config into an xml document
		XmlDocument doc = new XmlDocument();
		doc.LoadXml( config );

		// find the parameterized task node
		XmlElement node = ( XmlElement ) doc.SelectSingleNode( XPATH_PARAMS );
		if( node == null )
			return;

		// load the configuration
		NetReflectorTypeTable typeTable = NetReflectorTypeTable.CreateDefault();
		typeTable.Add( typeof( PropertyItem ) );
		typeTable.Add( typeof( ParameterizedNAntTask ) );

		// extract the current parameterized
		ParameterizedNAntTask task = ( ParameterizedNAntTask ) NetReflector.Read( node , typeTable );

		// update the tasks's properties and save the configuration
		task.Properties = properties.ToArray();

		// write out the xml to a string builder and load the xml into another node
		StringBuilder sb = new StringBuilder();
		using( XmlTextWriter writer = new XmlTextWriter( new StringWriter( sb ) ) )
		{
			NetReflector.Write( writer , task );
		}

		XmlDocument newDoc = new XmlDocument();
		newDoc.LoadXml( sb.ToString() );

		XmlElement clonedNode = ( XmlElement ) doc.ImportNode( newDoc.DocumentElement , true );
		XmlElement parent = ( XmlElement ) node.ParentNode;
		parent.ReplaceChild( clonedNode , node );

		// send the new xml/config back to the server
		sb = new StringBuilder();
		using( XmlTextWriter writer = new XmlTextWriter( new StringWriter( sb ) ) )
		{
			writer.Formatting = Formatting.Indented;
			doc.Save( writer );
		}

		string newXml = sb.ToString();
		_manager.UpdateProject( projectSpec , newXml );
	}	//END UpdateConfiguration method 

	/// <summary>
	/// Gets the property items that have been posted back
	/// </summary>
	private void GetUpdatedItems( IRequest req , List<PropertyItem> list )
	{
		bool keyFound = true;
		int counter = 0;

		// each property is defined dynamically in the html view via a text input element.
		// these elements follow the names "row0", "row1", etc, based on ordinal position
		// relative to the original list of items. 

		// loop until there are no more keys
		while( keyFound )
		{
			string key = string.Format( "row{0}" , counter );
			if( req.FindParameterStartingWith( key ) == string.Empty )
				break;

			string value = req.Params[ key ];

			list[ counter ].Value = value;
			counter++;
		}	//END loop
	}	//END GetUpdatedItems method 

	/// <summary>
	/// Commits the modified/updated property values back to the ccnet server
	/// </summary>
	/// <param name="properties"></param>
	/// <param name="cruiseRequest"></param>
	/// <returns></returns>
	private IResponse Commit( List<PropertyItem> properties , ICruiseRequest cruiseRequest )
	{
		IProjectSpecifier projectSpec = cruiseRequest.ProjectSpecifier;
		IServerSpecifier serverSpec = cruiseRequest.ServerSpecifier;
		IRequest req = cruiseRequest.Request;
		Hashtable map = new Hashtable();

		// get the list of updated property values and pass them back
		// to the ccnet server
		GetUpdatedItems( req , properties );
		UpdateConfiguration( projectSpec , properties );

		map.Add( "projectName" , cruiseRequest.ProjectName );
		map.Add( "props" , properties );

		return _viewGenerator.GenerateView( "ViewProjectProperties.vm" , map );
	}	//END Commit method 

	/// <summary>
	/// Generates a new view from scratch with the property items that are presently defined.
	/// </summary>
	private IResponse ExecuteNew( List<PropertyItem> properties , ICruiseRequest cruiseRequest )
	{
		Hashtable map = new Hashtable();
		map.Add( "props" , properties );
		map.Add( "projectName" , cruiseRequest.ProjectName );

		return _viewGenerator.GenerateView( "ViewProjectProperties.vm" , map );
	}	//END ExecuteNew method 

	#region IPlugin Members

	/// <summary>
	/// Returns the description for the link
	/// </summary>
	public string LinkDescription
	{
		get
		{
			return PLUGIN_DESCRIPTION;
		}
	}	//END LinkDescription property

	public INamedAction[] NamedActions
	{
		get
		{
			return new INamedAction[] { new ImmutableNamedAction( ACTION_NAME , this ) };
		}
	}	//END NamedActions property

	#endregion

}	//END PropertiesPlugin class 

Synopsis
The server side plugin wraps the built-in nant task. It adds support for explicit nant property definitions within the ccnet project configuration file. The dashboard plugin will use the server-side plugin to acquire these properties and to write the properties back to the configuration file.

Getting the NAnt Properties
Before the properties can be edited by the user, they must be enumerated from the ccnet project configuration file. This poses quite a challenge.

The problem is that the dashboard is executing in a different context from the ccnet server itself. The ccnet server is either running from the command line or as a service, and the dashboard is hosted by asp.net. There is a remoting api, but it’s pretty limited. The api doesn’t expose much functionality - it’s mostly limited to read-only access or kicking off builds. However, the api does provide limited access to a project configuration.

The ReadItems method uses this functionality to acquire the project configuration, and uses its own copy of the server-side plugin to deserialize the nant properties. It’s a bit of a hack, but it works.

Here’s the code listing for the ReadItems method:


/// <summary>
/// Reads all of the properties from the project's configuration
/// </summary>
private List<PropertyItem> ReadItems( IProjectSpecifier projectSpec )
{
	// read the project configuration, in xml form
	string config = _manager.GetProject( projectSpec );

	// load the config into an xml document
	XmlDocument doc = new XmlDocument();
	doc.LoadXml( config );

	// find the parameterized task node. If no node is defined, then return
	// an empty list of property items
	XmlElement node = ( XmlElement ) doc.SelectSingleNode( XPATH_PARAMS );
	if( node == null )
		return new List<PropertyItem>();

	// deserialize the custom server plugin from the xml node
	NetReflectorTypeTable typeTable = NetReflectorTypeTable.CreateDefault();
	typeTable.Add( typeof( PropertyItem ) );
	typeTable.Add( typeof( ParameterizedNAntTask ) );
	ParameterizedNAntTask task = ( ParameterizedNAntTask ) NetReflector.Read( node , typeTable );

	return new List<PropertyItem>( task.Properties );
}	//END ReadItems method 

The first line of the method acquires the project configuration. It calls the GetProject method on the ICruiseManagerWrapper interface(defined as the _manager member, set via constructor injection). The GetProject method accepts an IProjectSpecifier interface, which defines the project to retrieve. Fortunately, the IProjectSpecifier object reference is available from the ICruiseRequest the dashboard plugin is processing.

The return value of the GetProject method is a simple string. This string is the xml form of the project configuration.

This xml is loaded into an XmlDocument object. From there, a single node is selected from the XmlDocument via a SelectSingleNode call. The xpath is “project/tasks/parameterizedNant”. This gives us the actual xml node that represents the parameterizedNant server-side plugin. It’s important to understand that we are not actually interacting with the parameterizedNant object running within the ccnet process via remoting. Instead we’re going to be creating our own copy.

The next bit of code uses the NetReflector tricks used throughout the ccnet sourcecode. Ultimately all it does is deserialize the retrieved node into a new parameterizedNant object instance.

Finally the ReadItems method returns the object’s Properties value, which is List.

Which values were updated by the user?
So now we’ve retrieved the available nant property values from the project configuration. We’ve dumped them to the dashboard so the user can edit them(this work was covered in Post 5). When the user clicks the Submit button, we get a post back, and we need to determine which values actually changed.

Here’s the code listing for the GetUpdatedItems method:


/// <summary>
/// Gets the property items that have been posted back
/// </summary>
private void GetUpdatedItems( IRequest req , List<PropertyItem> list )
{
	bool keyFound = true;
	int counter = 0;

	// each property is defined dynamically in the html view via a text input element.
	// these elements follow the names "row0", "row1", etc, based on ordinal position
	// relative to the original list of items. 

	// loop until there are no more keys
	while( keyFound )
	{
		string key = string.Format( "row{0}" , counter );
		if( req.FindParameterStartingWith( key ) == string.Empty )
			break;

		string value = req.Params[ key ];

		list[ counter ].Value = value;
		counter++;
	}	//END loop
}	//END GetUpdatedItems method 

This code looks for a series of postback values named “row1″, “row2″, etc. We know the naming of these values because that’s how they are named in the template. The value names are sequenced, so if we can’t find the next key in the sequence, we know there are no more values to process.

The sequence of these keys are based on the ordinal position of the list of PropertyItem objects. So the first object will be represented by “row0″, the second “row1″, etc. If the key exists, we replace the corresponding PropertyItem object’s value with the key’s value.

Committing the changes
When the Submit button is clicked, the plugin commits all of the property values back to the configuration file, then re-generates the view with the updated property values. This behavior is facilitated by two methods: Commit, and UpdateConfiguration. The Commit method is pretty simple, but all of the real committing work takes place in UpdateConfiguration. Here’s the code listing:


/// <summary>
/// Updates the configuration
/// </summary>
private void UpdateConfiguration( IProjectSpecifier projectSpec , List<PropertyItem> properties )
{
	string config = _manager.GetProject( projectSpec );

	// load the config into an xml document
	XmlDocument doc = new XmlDocument();
	doc.LoadXml( config );

	// find the parameterized task node
	XmlElement node = ( XmlElement ) doc.SelectSingleNode( XPATH_PARAMS );
	if( node == null )
		return;

	// load the configuration
	NetReflectorTypeTable typeTable = NetReflectorTypeTable.CreateDefault();
	typeTable.Add( typeof( PropertyItem ) );
	typeTable.Add( typeof( ParameterizedNAntTask ) );

	// extract the current parameterized
	ParameterizedNAntTask task = ( ParameterizedNAntTask ) NetReflector.Read( node , typeTable );

	// update the tasks's properties and save the configuration
	task.Properties = properties.ToArray();

	// write out the xml to a string builder and load the xml into another node
	StringBuilder sb = new StringBuilder();
	using( XmlTextWriter writer = new XmlTextWriter( new StringWriter( sb ) ) )
	{
		NetReflector.Write( writer , task );
	}

	XmlDocument newDoc = new XmlDocument();
	newDoc.LoadXml( sb.ToString() );

	XmlElement clonedNode = ( XmlElement ) doc.ImportNode( newDoc.DocumentElement , true );
	XmlElement parent = ( XmlElement ) node.ParentNode;
	parent.ReplaceChild( clonedNode , node );

	// send the new xml/config back to the server
	sb = new StringBuilder();
	using( XmlTextWriter writer = new XmlTextWriter( new StringWriter( sb ) ) )
	{
		writer.Formatting = Formatting.Indented;
		doc.Save( writer );
	}

	string newXml = sb.ToString();
	_manager.UpdateProject( projectSpec , newXml );
}	//END UpdateConfiguration method 

This method literally does the opposite of the ReadItems method. It takes an IProjectSpecifier object reference and a list of PropertyItem objects, and updates the project’s configuration with the property values.

Like ReadItems, UpdateConfiguration gets the latest project configuration from the _manager member. It loads the xml into an XmlDocument object and deserializes the current parameterizedNant object from the xml.

From there, it sets the parameterizedNant object’s properties to be the new list of properties collected from the dashboard plugin. It then uses NetReflector to serialize the parameterizedNant object back to xml, injects that xml into the project configuration, and saves the configuration back to the ccnet server via a call to the ICruiseManagerWrapper’s UpdateProject method.

Viola! The properties that were just collected from the user will find themselves persisted to the project configuration file. We’re done!

Problem 1: Concurrency
The dashboard is a website, thus it is entirely possible that multiple users could commit modified properties back to the ccnet server at the same time. I’ve not tested this possibility, as my ccnet environment doesn’t get much traffic from users other than myself. I don’t know if ccnet will serialize these requests or what; all I know is that the dashboard plugin does not protect against this issue. Just keep that in mind.

Problem 2: Destructive Write Operation
The process of reading and writing the xml for the project configuration is destructive. Any whitespace and/or xml comments embedded within your project configuration file will be overwritten. I’m willing to accept this, but some people may not be.

Problem 3: Changes are Permanent
When a user changes a property value, that value is permanent. At least until it is explicitly modified by hand, or until some one changes the value again via the plugin. In other words, this plugin does not allow the nant script to use these properties for one-off builds.

For example, the entire reason I developed this plugin was so I could have a single project configuration produce either Debug or Release builds. As it stands now, if I switched the configuration from Debug to Release, all builds going forward will be Release builds. Of course, I could always change it back to a Debug build, but it will not happen automatically.

Consequently, this approach is not suitable for something like defining explicit version numbers for a build.

Next?
Until this point, I’ve posted all of the code in fragments. I would like to post all of the code in self-contained downloadable form, but I don’t know if that’s possible for reasons that I will not discuss at present. I know that this series of post is not the most organized, but I hope the information has been presented coherently enough to be of use to some one out there.

August 6, 2008

CruiseControl.NET and multiple build configurations: Part 6 - The server-side plugin

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 10:11 am

[More Posts On This Topic]

This is post 6 in a series devoted to extending CruiseControl.NET to support multiple build configurations for a single project. In this post, I will discuss a new component: the server-side plugin.

Up until now, we’ve limited our discussion to dashboard plugins. So far we’ve implemented a simple page that will allow a user to modify NAnt property values for a project. However, we are missing two very important pieces: the acquisition of the properties, and the committing of those properties once they have changed.

Unfortunately, we’re limited in what we can do with a dashboard plugin. The dashboard can be considered nothing more than a front-end for the actual ccnet service. It lets the user kick off builds, and inspect the status of current or past builds. It has some rather sophisticated reporting services, but provides little in the way of actually manipulating the ccnet service itself.

That entire paragraph is the long-winded way of saying that the dashboard api does not provide us with a means to really *do* anything with a given project. That’s why we need a server side plugin.

NAnt script properties
Now that we’ve decided that we would like our plugin to modify the properties for a nant script, we need to decide how to actually accomplish this work.

Those that are familiar with the existing ccnet/nant integration will recall that nant task has a property called BuildArgs. This property allows the configuration author to augment how nant executes a script by passing nant command line arguments. We will use this property and a bit of inheritance.

Custom Task
I’ve created a new class called ParameterizedNAntTask which implements ccnet’s ITask interface. This task will be substitute your existing nant task references in your ccnet configuration. This task will internally delegate to the existing NAnt task, so we benefit from the existing nant integration behavior. Additionally, the task will expose an array of PropertyItem objects(this class was introduced in the last post). When this task is executed, the properties will be passed to the inner nant task via the BuildArgs property, so that the properties are injected into the nant script at runtime.

The full code listing for this class follows:


/// <summary>
/// This object is the heart of the server side plugin. This object implements the core ITask interface, and
/// wraps/delegates CCnet's built-in nant task.
///
/// This task allows the delegated nant task to be paramterized.
/// </summary>
[ReflectorType( "parameterizedNant" )]
public class ParameterizedNAntTask : ITask
{

	/// <summary>
	/// The inner nant task
	/// </summary>
	private NAntTask _innerTask;

	/// <summary>
	/// The properties for the build
	/// </summary>
	private PropertyItem[] _properties;

	/// <summary>
	/// Gets/Sets the inner nant task
	/// </summary>
	[ReflectorProperty( "nant" , Required = true )]
	public NAntTask InnerTask
	{
		get
		{
			return _innerTask;
		}
		set
		{
			_innerTask = value;
		}
	}	//END InnerTask property

	/// <summary>
	/// Gets/Sets the array of property objects that will be passed to the inner nant task
	/// </summary>
	[ReflectorArray( "properties" )]
	public PropertyItem[] Properties
	{
		get
		{
			return _properties;
		}
		set
		{
			_properties = value;
		}
	}	//END Properties property

	#region ITask Members

	/// <summary>
	/// Delegates to the inner nant task
	/// </summary>
	public void Run( IIntegrationResult result )
	{
		// set the nant tasks's build args; the existing object's build args will be replaced
		string argText = GetBuildArgs();
		_innerTask.BuildArgs = argText;

		_innerTask.Run( result );
	}	//END Run method 

	#endregion

	/// <summary>
	/// Converts the defined property items into name/value pairs that will be
	/// passed to nant via the command line(see the -D: nant switch)
	/// </summary>
	/// <returns></returns>
	private string GetBuildArgs()
	{
		StringBuilder sb = new StringBuilder();

		foreach( PropertyItem item in Properties )
		{
			string arg = string.Format( " -D:{0}={1}" , item.Name , item.Value );
			sb.Append( arg );
		}	//END property loop 

		return sb.ToString();
	}	//END GetBuildArgs method 

}	//END ParameterizedNAntTask class 

The first thing of note is the ReflectorType attribute; I’ve decorated the class with this attribute, naming it “parameterizedNant”. Remember, this value is how the task will be referred from the configuration file.

The task has only two members: a private inner NAntTask object, and an array of PropertyItem objects. Each of these members are accessed via public properties.

The NAntTask object reference is wrapped via the InnerTask public property(read/write). This property is decorated with the ReflectorProperty attribute, named “nant”, and is required.

The PropertyItem array is accessed via the Properties public property. This property is decorated via the ReflectorArray attribute, which is likewise named “properties”.

** This custom task does not instantiate either of these members. We are relying on ccnet to inject these object references via the property setters.

That leaves two methods: ITask.Run, and GetBuildArgs.

GetBuildArgs
The GetBuildArgs method returns a simple string that will contain all of the property declarations. This value will be provided to the inner nant task’s BuildArgs property so the property values will be injected into the script at runtime.

It’s a fairly simple method: it simply loops through each PropertyItem in the Properties array, appending the property declaration in the form of ” -D:[name]=[value]“. Notice that each property value is not quote-escaped; so the property values within the configuration itself should be quote-escaped as necessary.

ITask.Run
The ITask interface defines a single method: void Run( IIntegrationResult ). Fortunately for us, we don’t need to figure out how to work with the IIntegrationResult interface because we’re delegating to an existing ITask implementation that already knows how to do so. Our implementation of this method simply calls the GetBuildArgs method to get an argument list of properties, and sets the inner nant task’s BuildArgs property to this value. Then it delegates to the inner task’s Run method. Pretty simple.

So what does this look like in a configuration file?
You’ve seen the code for the server side plugin. I hope it makes sense. But to put it into context, here is a bit of sample xml that would be used in the configuration file:


<cruisecontrol>
  <project>

    <tasks>
      <parameterizedNant>
        <nant>
          <buildArgs />
          <buildFile>main.build</buildFile>
          <buildTimeoutSeconds>1320</buildTimeoutSeconds>
          <baseDirectory />
          <executable>nant.exe</executable>
          <logger>NAnt.Core.XmlLogger</logger>
          <nologo>True</nologo>
          <targetList />
        </nant>
        <properties>
          <property>
            <name>build.release</name>
            <value>false</value>
          </property>
          <property>
            <name>prop2</name>
            <value />
          </property>
          <property>
            <name>prop3</name>
            <value />
          </property>
        </properties>
      </parameterizedNant>
    </tasks>

  </project>
</cruisecontrol>

Notice that the parameterizedNant declaration is inserted before the normal nant declaration. In this case, you would define your nant setup normally. After the nant declaration is the element, which contains a series of elements.

So from this sample xml, and from the code listing, one can infer that when ccnet kicks off this project, it will execute nant and pass it the “build.release”, “prop2″, and “prop3″ property values via the command-line.

What’s next?
We have a dashboard plugin. We now have a server-side plugin. What’s next? Now we need to find a way for the dashboard plugin to augment the configuration for a project and set the embedded property values for the parameterizedNant task. That is the object of the next post.

August 5, 2008

CruiseControl.NET and multiple build configurations: Part 5 - My custom template

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 3:38 pm

[More Posts On This Topic]

It’s been a long few months since my last post in this series. For that I apologize. Today I resume the series with the discussion of the custom template I’ve used for my properties plugin.

Let’s not waste time and jump right into the template. It’s not very large:


<h2>NAnt properties for project $projectName</h2>

<table>
	<tr>
		<td width="20" />
		<td width="350" >The properties defined on this page are passed to the NAnt executable via the command line as
		NAnt properties.</td>
	</tr>
</table>
<br />
#if ($props.Count == 0)
	No properties
#else
	<form method="post">
	<table class="section-table" cellspacing="0" border="0">
		<tr class="sectionheader">
			<td width="150">Property Name</td>
			<td width="200">Value</td>
		</tr>
		#set( $rowCounter = 0 )
		#foreach( $prop in $props )

			#if ( $rowCounter % 2 == 0 )
				#set( $rowClass = 'section-oddrow' )
			#else
				#set( $rowClass = 'section-evenrow' )
			#end

			<tr class="$rowClass">
				<td>$prop.name</td>
				<td><input type="text" name="row$rowCounter" value="$prop.value"></td>
			</tr>

			#set( $rowCounter = $rowCounter + 1 )
		#end
		<tr>
			<td colspan="2" align="right"><br /><input type="submit" name="Refresh" value="Commit"/></td>
		</tr>

	</table>

#end

Before we examine the template, here is the output of the template:

Output of the template

Output of the template

As you can see, it’s a simple and dirty page that’s not very pretty. However, it is functional. Now that you’ve seen the complete template and it’s output, lets examine it piece-by-piece.

Header
The header section of the template looks like this:


<h2>NAnt properties for project $projectName</h2>

<table>
	<tr>
		<td width="20" />
		<td width="350" >The properties defined on this page are passed to the NAnt executable via the command line as
		NAnt properties.</td>
	</tr>
</table>
<br />

The template starts out with a simple H2 header element, and displays the name of the project via a reference to the $projectName variable. After that, it outputs a short description of what this page does. Pretty simple html so far.

No properties defined?
The next section of the template looks like this:


#if ($props.Count == 0)
	No properties
#else

This section determines if there are any property objects available for the template. The $props variable will be published to the template via the plugin, so we can refer to it here. If there are no properties, then we just output the text “No properties”. Short and sweet.

What’s interesting to note here though is Velocity’s support of dot-notation evaluation. Notice the text “$props.Count”? This text causes Velocity to evaluate the $props object for a property called Count. So as long as the $props object has a public property named Count and can be compared to the literal ‘0′, we’re in good shape. We need to remember that when we advertise the object to the template when the template is generated.

Forms and post-back
The next bit of code declares a form, containing a table and a couple of table columns. Notice the form declaration has the method defined, but is not named nor does it define an action. I found that this declaration allows the form to post back to itself; actually, I borrowed this behavior from other templates distributed with the ccnet dashboard.

Displaying the defined properties
The rest of the template simply loops through all of the properties defined, and outputs the name and value of each.

Notice the use of the $rowCounter variable? It’s defined before the looping begins. For each iteration of the loop, $rowCounter is evaluated by modulus 2 to determine if we are on an odd or even row, and sets the style accordingly. This lets each row be shaded on or off.

Additionally, the $rowCounter variable serves another purpose: it allows us to refer to each input box for each property’s value. When the property name and value are emitted, the value is placed within an input text element. The name of the text element takes the form of “row$rowCounter”, which will expand at runtime to be “row0″, “row1″, etc. We’ll use that later in the plugin when processing the postback.

Here’s the the code listing for the rest of the template:


<form method="post">
	<table class="section-table" cellspacing="0" border="0">
		<tr class="sectionheader">
			<td width="150">Property Name</td>
			<td width="200">Value</td>
		</tr>
		#set( $rowCounter = 0 )
		#foreach( $prop in $props )

			#if ( $rowCounter % 2 == 0 )
				#set( $rowClass = 'section-oddrow' )
			#else
				#set( $rowClass = 'section-evenrow' )
			#end

			<tr class="$rowClass">
				<td>$prop.name</td>
				<td><input type="text" name="row$rowCounter" value="$prop.value"></td>
			</tr>

			#set( $rowCounter = $rowCounter + 1 )
		#end
		<tr>
			<td colspan="2" align="right"><br /><input type="submit" name="Refresh" value="Commit"/></td>
		</tr>

	</table>

#end

Great - we have a template. Now what?

Generating the template
We’ve already covered how templates are generated at runtime by the dashboard. Now we can discuss how to generate this specific template from the properties plugin.

The first thing we need to do is to determine if the template is posting-back, or if the template is being generated from scratch. Why? Because if it’s a post back, we need to commit/save the modified properties, otherwise we need to publish the properties to the template afresh. So how do we do that? More specifically, how does the dashboard tell us that the request is postback?

Fortunately for us, the dashboard stuffs a parameter into the request called “Refresh”. So all we have to do is check the request for this parameter. It doesn’t matter matter what the parameter’s value is. If the parameter exists, it’s a postback. Now we need to know how to interrogate the request for parameters. Fortunately the IRequest interface is not that complicated, and it exposes a method named FindParameterStartingWith. From what I can determine, this method does not actually return the parameter value, merely the parameter key. So, using this behavior, we can define a new method for our plugin:


/// <summary>
/// Determines if the current page view is a normal view, or if the form has
/// been submitted as a postback(and subsequently the modified property values will
/// need to be committed back to the ccnet server).
/// </summary>
private bool IsRefresh( IRequest request )
{
  string key = request.FindParameterStartingWith( "Refresh" );
  return key != string.Empty;
}	//END IsRefresh method

Now that we know if the page is a postback or not, what do we do next? Since we’re publishing properties, we need to know what properties to publish to the template. Additionally, if the request is indeed a postback, we need to commit the saved properties.

Here’s my full implementation of the ICruiseAction.Execute method:


/// <summary>
/// Executes the request
/// </summary>
public IResponse Execute( ICruiseRequest cruiseRequest )
{
	// get the request and the current project
	IRequest req = cruiseRequest.Request;

	// read all of the properties as they are currently defined in the project's config
	List<PropertyItem> existingProperties = ReadItems( cruiseRequest.ProjectSpecifier );

	// determine if we are displaying the properties, or committing the properties
	if( IsRefresh( req ) )
		return Commit( existingProperties , cruiseRequest );
	else
		return ExecuteNew( existingProperties , cruiseRequest );
}	//END Execute method 

This is a pretty short method implementation. The first thing it does it acquire the actual IRequest reference. Next it reads the properties that are defined for the current project(notice the reference to the cruiseRequest.ProjectSpecifier property?). Afterwards, it calls the IsRefresh method(which we already detailed) and either calls Commit or ExecuteNew. Let’s not worry about the Commit method just yet - it will demand another post entirely. For now let’s concentrate on the calls to ReadItems and ExecuteNew.

ReadItems method
From the Execute implementation, you can infer that the ReadItems method returns a List of type PropertyItem. The PropertyItem class is very simple, and I won’t waste time discussing it in detail. The full listing follows:


/// <summary>
/// This type reflects custom properties defined in the config file, as child elements to the
/// parameterizedNant task. PropertyItem objects are simple name/value pairs.
/// </summary>
[ReflectorType( "property" )]
public class PropertyItem
{
	/// <summary>
	/// The name of the property
	/// </summary>
	private string _name;

	/// <summary>
	/// The value of the property
	/// </summary>
	private string _value;

	/// <summary>
	/// Default constructor
	/// </summary>
	public PropertyItem()
		: this( string.Empty , string.Empty )
	{
	}	//END constructor 

	/// <summary>
	/// Parameterized constructor to initialize the object with its name/value pair
	/// </summary>
	/// <param name="name">the name of the property</param>
	/// <param name="value">the value of the property</param>
	public PropertyItem( string name , string value )
	{
		_name = name;
		_value = value;
	}	//END constructor 

	/// <summary>
	/// Gets/Sets the name of the property
	/// </summary>
	[ReflectorProperty( "name" )]
	public string Name
	{
		get
		{
			return _name;
		}
		set
		{
			_name = value;
		}
	}	//END Name property

	/// <summary>
	/// Gets/Sets the value of the property
	/// </summary>
	[ReflectorProperty( "value" )]
	public string Value
	{
		get
		{
			return _value;
		}
		set
		{
			_value = value;
		}
	}	//END Value property

	public override string ToString()
	{
		return string.Format( "{0}={1}" , _name , _value );
	}	//END ToString method 

}	//END PropertyItem class 

The ReadItems needs to read the nant properties defined for a given project configuration. Out of the box, ccnet does not give us this behavior. That means we will be developing a server-side plugin that is a sister to the dashboard plugin. Like the implementation of the Commit method, that topic is *way* too deep for this post. So for right now, just implement the ReadItems method so it returns a mock list of PropertyItem objects.

ExecuteNew method
This post is getting pretty long, so fortunately the ExecuteNew method is pretty simple. The purpose of the ExecuteNew method is to take a list of PropertyItem objects, and publish them to the template so they can be available at evaluation time. This method is only executed for new page requests, and not for postbacks. Here’s the full implementation of the ExecuteNew method:


/// <summary>
/// Generates a new view from scratch with the property items that are presently defined.
/// </summary>
private IResponse ExecuteNew( List<PropertyItem> properties , ICruiseRequest cruiseRequest )
{
	Hashtable map = new Hashtable();
	map.Add( "props" , properties );
	map.Add( "projectName" , cruiseRequest.ProjectName );

	return _viewGenerator.GenerateView( "ViewProjectProperties.vm" , map );
}	//END ExecuteNew method 

When you looked at the template, did you think it would be difficult to add a list of objects? Fortunately for us, it’s exceedingly simple. Remember that the hashmap published to the template is a simple string/object affair. So to publish the properties, we simply add them to the hashtable with the key of “props”. After that, it’s nothing that hasn’t already been covered.

Here would be a good time to point out how stupid it is to use string literals. Just remember, this is nothing more than a blog post :).

** Styles
The template I list earlier referenced a few different styles. These styles are defined within the file “cruisecontrol.css” and is in the root of the dashboard’s folder. I’m sure you are free to define your own styles in your template or even in an external css file, but I just played around with the template until I found something that wasn’t too obnoxious.

The next post will cover the topic of the server-side plugin. At this point you’re probably wondering why we even need one?

April 28, 2008

CruiseControl.NET and multiple build configurations: Part 4 - Velocity Templates

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 3:16 am

[More Posts On This Topic]

This is post 4 in a series devoted to extending CruiseControl.NET to support multiple build configurations for a single project. In this post, I will cover the use of the Velocity engine and outputting responses from the plugin.

ICruiseAction.Execute method
The second post in this series addressed the interfaces that your plugin is required to implement. The main method to implement is the ICruiseAction.Execute method, which accepts an ICruiseRequest object and returns an IResponse object. What exactly is an IResponse, and how can your plugin create it?

As mentioned in other posts, if you ever have questions about how something works, I suggest that you consult the ccnet source code. The main reference that I’ve been using is the ProjectReportProjectPlugin class. Examination of this class shows that it uses an object called IVelocityViewGenerator to generate responses. It acquires the IVelocityViewGenerator reference from its constructor(injected by the runtime).

IVelocityViewGenerator - How is it used?
The following code has been borrowed from the ProjectReportProjectPlugin’s Execute method implementation:


Hashtable velocityContext = new Hashtable();

velocityContext["projectName"] = projectSpecifier.ProjectName;
velocityContext["externalLinks"] = farmService.GetExternalLinks(projectSpecifier);
velocityContext["noLogsAvailable"] = (buildSpecifiers.Length == 0);

return viewGenerator.GenerateView(@"ProjectReport.vm", velocityContext);

Take a moment and let the code sync in. The object is only used once, in the method’s return statement. The interface is rather simple, and has but a single method. The interface follows:


public interface IVelocityViewGenerator
{
  HtmlFragmentResponse GenerateView(string templateName, Hashtable velocityContext);
}

If you examine the HtmlFragmentResponse object, it implements the IResponse interface, which is the return type for the ICruiseAction.Execute method.

The GenerateView method accepts a template name, and a velocityContext; a basic hashtable. The return statement from the code listing shows a string literal, “ProjectReport.vm”, and a hashtable.

The hashtable that is passed to the GenerateView method has been populated with a number of items, such as the project name. The hash table is keyed on string values, and the values are objects.

Now that you know what the GenerateView method accepts, what do you think the arguments actually are? What is “ProjectReport.vm”, and what is that hashtable?

Velocity & Templates
The literal “ProjectReport.vm” is obviously a filename, so I looked through the ccnet distro for the file. The file is located in the dashboard’s templates directory, along with a bunch of other .vm files. It’s a simple text file, that appears to be some sort of html template. The full source for the file follows:


<h2>Project Report for $projectName</h2>

#if ($externalLinks.Length > 0)
<table class="ExternalLinks">
	<tr>
		<th>External Links</th>
	</tr>
	#foreach ($externalLink in $externalLinks)
	<tr bgcolor="Ivory">
		<td align="Left">
			<a href="$externalLink.Url">$externalLink.Name</a>
		</td>
	</tr>
	#end
</table>
#end

<p>
#if ($noLogsAvailable)
There are currently no build logs available for this project - make sure your Build Server configuration is correct and that this Project's build is not throwing any exceptions
#else
	#if ($pluginInfo)
	<h3>Most Recent Build Results</h3>
	$pluginInfo

	Click <a href="$mostRecentBuildUrl">here</a> for the full build report.
	#else
	Click <a href="$mostRecentBuildUrl">here</a> for the most recent build report.
	#end
#end
</p>

Like I said, it appears to be html. However, scattered throughout the source are various directives. Statements or commands appear to begin with the pound sign, while property/variable references appear to begin with a dollar sign. In fact, the very first line of the template refers to a property/variable named $projectName. Does that ring a bell? If you refer to the first code listing, the velocityContext hashmap has a key named “projectName”.

Putting two and two together, its apparent that the IVelocityViewGenerator.GenerateView method takes the name of a template, and the second argument, the velocityContext hashmap, contains references that can be expanded when the template is processed.

This templating system piqued my interest, and I discovered that Velocity is the name of a templating project hosted by Apache. The official home page is available here. The reference is here, and the user’s guide is here. These resources will prove to be very valuable as you develop your own velocity templates.

And one more thing. This is a dotnet project, so the original java version of Velocity isn’t being used. One of the project references for the dashboard is an assembly named NVelocity. A quick google search of that term yield the home page for the project, which is a dotnet port of Velocity. I haven’t fully researched the project, so I don’t know how complete it is, nor do I know the differences. This definitely deserves further investigation.

Conclusion
You should now know how the ICruiseAction.Execute method behaves, and how to generate responses via the velocity runtime. I’ve also provided the docs and reference for Velocity, which will aid you in composing your own custom templates.

The next post will cover the template that I have developed for my build configuration plugin.

April 23, 2008

CruiseControl.NET and multiple build configurations: Part 3 - Dependency Injection

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 3:09 pm

[More Posts On This Topic]

This post is part 3 in a series devoted to extending CruiseControl.NET to support multiple build configurations for a single project. In this post, I will cover a single topic: ccnet’s method of dependency injection.

Constructor Dependency Injection
The ccnet docs for developing dashboard plugins contains a section titled “Defining dependencies”, which states:

The CruiseControl.NET Web Dashboard uses a Constructor Dependency Injection (CDI) pattern to enable classes to define what types they are dependent on. The Dashboard API has a number of types you can depend upon which can do things like return you the currently viewed build log. You can also specify dependencies to your own types. It is recommended that you use interfaces to define responsibilities.
The Dashboard’s CDI implementation does not currently allow for runtime configuration. It will use sensible defaults where available. We plan on adding more configuration later.

That’s all great, but what does it mean? From my first pass of the docs, I understood that somehow my constructor would be analyzed and the correct object references would be provided to my plugin. A fine theory, but how does it work in practice? Specifically, *which* objects would I receive?

Again I consulted the ccnet’s source code. I referred to the handy ProjectReportProjectPlugin class, and found the following constructor:


public ProjectReportProjectPlugin(IFarmService farmService, IVelocityViewGenerator viewGenerator, ILinkFactory linkFactory)
{
}

This class is receiving a reference to the IFarmService interface, IVelociyViewGenerator interface, and the ILinkFactory interface. I copied these same arguments into the constructor for my own plugin, and attached to the asp.net process. I refreshed the dashboard page, caught the break point in the debugger, and sure enough, my constructor received a valid object reference for each argument. It must be magic.

Constructor Argument Type Availability
I’ve now established that at the minimum I can receive valid object references for three interfaces. But what else is available? At this point, I didn’t know what any of these interfaces actually *did*, so I was worried about obtaining dashboard services that might or might not be available through these interfaces.

Still attached to the debugger, I used the Call Stack window to go backwards and find out what was actually instantiating my plugin. This technique is pivotal; you will use this technique many times during the development of your plugin, depending on the services your plugin requires.

Following the call stack backwards shows a bunch of methods being executed within the NetReflector assembly, and the first method one can find that is debuggable is ThoughtWorks.CruiseControl.WebDashboard.DashboardConfigurationLoader.Load method. The last line of the method looks like this:


return NetReflector.Read(node, typeTable);

Evidently, the dashboard is relying upon the NetReflector runtime to deserialize all of the plugins from the dashboard config file. The read method accepts an xml node, and a NetReflectorTypeTable. This guy proves very interesting.

The NetReflectorTypeTable has a member named “instantiator”, of type ObjectionNetReflectorInstantiator. That object has a member named “objectStore”, of type ObjectionStore. All of these objects and members are viewable from the runtime type inspector.

The ObjectionStore apparently contains all of the reflection information and available object instances. The ObjectionStore has a hashtable named “implementationTypes”, and it is this hashtable that I used to determine which objects were available to be injected into my constructor.

My implementationTypes map has 17 types available. I have no idea if these types are universal; I imagine the contents of the implementationTypes table will vary depending on the deployment. Inspection of my copy of this table yields the following types:

ThoughtWorks.CruiseControl.WebDashboard.IO.ICruiseRequest
ThoughtWorks.CruiseControl.Core.Reporting.Dashboard.Navigation.IPhysicalApplicationPathProvider
ThoughtWorks.CruiseControl.Remote.ICruiseManagerFactory
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.ILinkFactory
ThoughtWorks.CruiseControl.Core.Reporting.Dashboard.Navigation.ICruiseUrlBuilder
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IAllBuildsViewBuilder
ThoughtWorks.CruiseControl.WebDashboard.MVC.View.IVelocityViewTransformer
ThoughtWorks.CruiseControl.WebDashboard.ServerConnection.ICruiseManagerWrapper
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IProjectGrid
ThoughtWorks.CruiseControl.WebDashboard.ServerConnection.IFarmService
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.ILinkListFactory
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IProjectGridAction
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IActionInstantiator
ThoughtWorks.CruiseControl.WebDashboard.Configuration.IDashboardConfiguration
ThoughtWorks.CruiseControl.Core.Util.IMultiTransformer
ThoughtWorks.CruiseControl.WebDashboard.MVC.View.IVelocityViewGenerator
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IBuildNameFormatter

I really don’t know what most of these objects do, but it’s not too hard to figure out when you have the source code.

Now you know how to determine what types are available for your constructor at runtime. From here, you can consult the source code to determine what services these interfaces provide, and parameterize your constructor accordingly.

The next post will cover the use of the Velocity subsystem.

April 22, 2008

CruiseControl.NET and multiple build configurations: Part 2 - Starting the dashboard plugin

Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 10:11 am

[More Posts On This Topic]

This post continues a thread about how to support multiple build configurations from CruiseControl.NET and NAnt.

My goal was to expose some method through the web dashboard by which the user could select a build configuration and kick off a build. To accomplish that goal, some modification or extension of the dashboard is required. I decided against modifying the actual ccnet source, and instead chose to develop a dashboard plugin.

I started with the official ccnet docs regarding the subject, available here. I suggest readers acquaint themselves with the material.

Plugins Won’t Load
Before I get to the meat of the post, I must warn you that as it stands *today*, custom dashboard plugins will not load. The documentation states that plugin assemblies should be named “ccnet.*.plugin.dll”, and be placed in the dashboard’s bin folder. Presently, the code for loading plugins for the dashboard looks for assemblies via the path returned from Assembly.GetExecutingAssembly().Location(the Assembly.GetExecutingAssembly() call refers to the actual dashboard assembly). This line of code is logical, but it returns the path of the assembly as it is hosted by asp.net, which is some deep-dark directory buried within Windows because of asp.net’s shadow copy.

The dashboard’s source code must be changed. The Assembly.CodeBase property should be used instead, which returns a string uri to the assembly. This uri then needs to be parsed to obtain the actual physical location of the assembly so that satellite assembly lookup can work. I started a thread in the ccnet-devl groups. A ticket was opened, and as far as I know, the issue has been corrected. I suspect the correct code will be present for version 1.4, whenever that is released. Please note, however, that that is my assumption only, and has not been promised in any way by the ccnet team. Details on the defect, and how to fix it, are available here. Also please note that until the correction for the defect is available, you must pull down a copy of the source code, make the fix yourself, and deploy the updated dashboard binary.

Goal
I’ve already established my goal. Now let me describe how I will achieve that goal. Building upon my previous post about using NAnt properties to build debug or release configurations via the csc NAnt task, I have decided that I would like to allow the user to modify the initial values of NAnt properties for each configured ccnet project. The modified values for these properties must ultimately be recognized by ccnet so that these values can be passed to the root NAnt build script for the project when it is launched. For these reasons I have named my dashboard plugin “PropertiesPlugin”.

Source Code & Debugging
It’s possible to develop a dashboard plugin without benefit of the ccnet source code, but I strongly encourage you to download the source as it will make things much easier. For my development, I used version 1.2.1 of the source.

To debug your plugin, you will need to make sure you have the ccnet server running(either as a service or from the console), and you will need to attach to the aspnet_wp.exe process. Make sure that the build output of the project is the dashboard’s bin directory, otherwise the debugger won’t hit your breakpoints because the assembly will be out of date. Another issue that I sometimes face is that objects provided to your plugin from the dashboard will not evaluate and cannot be browsed. In such cases, I use the IIS control panel applet to stop/start the ccnet www app, which usually corrects the problem.

Starting the plugin, interfaces, and NetReflector
To start the plugin, create a public class that implements the ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.ICruiseAction interface, and the ThoughtWorks.CruiseControl.WebDashboard.Dashboard.IPlugin interface. Additionally, the class must be decorated with the ReflectoryType attribute(exposed via the Exortech.NetReflector interface).

At a minimum, your plugin assembly will require references to NetReflector.dll, ThoughtWorks.CruiseControl.Core.dll, ThoughtWorks.CruiseControl.Remote.dll, and ThoughtWorks.CruiseControl.WebDashboard.dll (each is present in the dashboard’s bin folder).

Example:


[ReflectorType( "propertiesPlugin" )]
public class PropertiesPlugin : ICruiseAction , IPlugin
{
  private const string PLUGIN_DESCRIPTION = "NAnt Project Properties";

  private const string ACTION_NAME = "ViewProjectProperties";

  #region IPlugin Members
  public IResponse Execute( ICruiseRequest cruiseRequest )
  {
    return null;
  } //END Execute method
  #endregion

  #region IPlugin Members

  /// <summary>
  /// Returns the description for the link
  /// </summary>
  public string LinkDescription
  {
    get
    {
      return PLUGIN_DESCRIPTION;
    }
  } //END LinkDescription property

  public INamedAction[] NamedActions
  {
    get
    {
      return new INamedAction[] { new ImmutableNamedAction( ACTION_NAME , this ) };
    }
  } //END NamedActions property
}

The above code is the bare minimum for creating a plugin. As it stands, it will not execute correctly, because the ICruiseAction.Execute implementation returns null. Ultimately we will need to return a valid IResponse by using the Velocity view generator, but I’ll touch on that subject later. Regardless, at this point the plugin should compile, and you should be able to debug it.

ReflectorType Attribute
As noted earlier, the class is decorated with the ReflectorType attribute. This attribute is significant. When the dashboard loads the configuration(dashboard.config), it will attempt to load named plugins from all assemblies(internal and external) via NetReflector. This plugin is named “propertiesPlugin”, and NetReflector will look for any public types decorated with this value.

ICruiseAction interface
This class implements the ICruiseAction interface. This interface is used to perform any number of actions from the dashboard user interface. Almost every link you click within the dashboard is represented by an accompanying action implementation.

This interface defines but a single method: IResponse Execute( ICruiseRequest req ). The basic idea is that the action accepts the current request and generates a response. It’s pretty easy really. The request identifies all kinds of things, including the current ccnet server and project. The response is expected to be consumable html that will ultimately be sent to the browser. More on that later.

IPlugin interface
The IPlugin interface is what allows the dashboard to expose your plugin via its user interface. The IPlugin interface is rather simple:


interface IPlugin
{
  string LinkDescription
  {
    get;
  }

  INamedAction[] NamedActions
  {
    get;
  }
}  

The LinkDescription property is the text for the link as it will be displayed to the user. The INamedAction[] property returns an array of named actions that will be executed when the link is clicked by the user.

I have not yet experimented with what happens when more than one named action is returned in the array, but I assume the actions are executed in succession. I do wonder however what happens to the IResponse from each action’s Execute method. Are they just appended to the stream? Unknown.

In the previous example, the plugin’s LinkDescription implementation simply returns a constant value. However, examine the NamedActions implementation:


public INamedAction[] NamedActions
{
  get
  {
    return new INamedAction[] { new ImmutableNamedAction( ACTION_NAME , this ) };
  }
}	

This code defines an INamedAction array inline, with only a single element. That element is of type ImmutableNamedAction, with an action name and a reference to the actual action implementation(this - the plugin).

At this point I would like to explain that I didn’t inherently know to use the ImmutableNamedAction class. In fact, I didn’t know how to implement this property. To figure that part out, I consulted the dashboard source code, and found the ProjectReportProjectPlugin class. I saw how it implemented this property, and borrowed its behavior.

Regardless, it is important to note the two entities being used by the dashboard here, and the interaction of these entities.

The dashboard will load the configuration, see that a plugin is defined, and load the plugin. This plugin must implement the IPlugin interface. This interface allows the dashboard to expose the plugin to the user interface. It also instructs the dashboard as to what actions are available from the plugin.

The actions for the plugin are altogether different from the plugin. An action is what happens when the link for the plugin is clicked. Notice the distinction?

In this plugin, the action and the plugin are the same object, only because this plugin is very simple.

Plugin Configuration
The documentation states that dashboard plugins can take one of four forms:

  • Farm Plugin
  • Server Plugin
  • Project Plugin
  • Build Plugin
  • More information about what type of plugin does what is available in the documentation. For my purposes, I chose to implement a project plugin because I wanted to be able to modify the build properties for each project independently.

    After you have implemented the basic plugin shell and deployed the assembly, you must configure the dashboard to recognize the plugin. This topic is also covered in the documentation. But for the purposes of this plugin, the configuration was very simple. I simply added a node named “propertiesPlugin” as a child element to the “projectPlugins” node(in the file dashboard.config). Remember, this node name - projectPlugins - must match the value of the ReflectorType attribute.

    An example dashboard config follows(boilerplate stuff exempted):

    
    <dashboard>
      <plugins>
        <farmPlugins>
          ...
        </farmPlugins>
        <serverPlugins>
          ...
        </serverPlugins>
        <projectPlugins>
    
          <!-- this declaration instructs the dashboard to include the project plugin -->
          <propertiesPlugin />
    
        </projectPlugins>
      </plugins>
    </dashboard>
    

    That’s it for now. The above code(along with the dashboard source code fix) should get any one started with a very basic project plugin. I will detail the Velocity stuff and ccnet’s use of constructor dependency injection in the next post.

    April 21, 2008

    CruiseControl.NET and multiple build configurations: Part 1 - Introduction

    Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 3:28 pm

    [More Posts On This Topic]

    I recently created a post regarding handling debug or release configurations within nant scripts. This post will extend the problem to CruiseControl.NET.

    Question: how does one manage automated builds within CruiseControl.NET where multiple configurations are present?

    I’ve been considering this problem for a couple of years now, and a solution that I liked has evaded me, until now.

    My first thought was to have separate projects configured, each pointing to the same project, but with its own configuration. I even implemented this approach, once, and it lasted until my first test pass. One project performed a DEBUG build, while another configuration performed a RELEASE build. When I committed a change to the repository, ccnet dutifully observed the change and kicked off both builds. But as both builds shared the same physical location on disk, both build scripts attempted to create and delete temporary build folders, resulting in file locking errors.

    I considered having completely separate folders on disk for each project configuration, both pointing to the same module in the CVS repository, but soon dismissed the idea. It seemed like a waste of both disk space and cycles. Disk space is cheap, and so are processor cycles(my builds are for the most part very quick), but I didn’t want to follow this same duplicated-folder pattern for each of my projects. After all, my build environment is presently handling about 10 separate projects. Duplicating each project for DEBUG or RELEASE configurations would be a shameful waste.

    I’ve played around with the idea of creating a custom ccnet plugin, but the documentation is quite poor and I didn’t have the luxury of devoting time or concentration to the issue. That is, until very recently.

    I reviewed the public ccnet docs again, researching plugins. I could create a plugin hosted by the server process, or I could create a plugin for the dashboard. I chose to implement a dashboard plugin because ultimately I wanted the ability for the user to choose a configuration from the dashboard itself, and to kick off a build after selecting the desired configuration.

    My first thought of a dashboard plugin was to modify the main farm page of the dashboard; the page with the project grid that allows users to force builds, or stop active builds. I wanted to add a new column to the table with a drop-down box that contained the available configurations. The user would choose the configuration, and click the Force button to perform a specific-configuration build.

    It took me a while to come to grips with creating a dashboard plugin. The documentation is sparse, and the object model difficult to understand, specifically the model of obtaining object references at runtime.

    My first attempt ultimately proved be a failure, but it wasn’t completely unfruitful. I learned a bit of how to use the Velocity system to generate views, and how object references are injected into your plugin’s constructor at runtime. I also uncovered a bug within the ccnet dashboard itself.

    Fortunately, this bit of code-spelunking and discovery didn’t take very long. Overall I believe I spent maybe 10 hours of clock-time to end up with a compiled and deployed solution that meets my needs. I will document my discoveries, as well as the final solution, over the coming days. I believe blogging about this process will prove interesting, as I predict it will take more time to sufficiently cover all of the topics than it took to create a final solution in the first place.

    NAnt build scripts for C#: Debug or Release configurations

    Filed under: Continuous Integration/CruiseControl.NET, NAnt, c#, dotnet — treyhutcheson @ 1:19 pm

    When one begins to delve into the use of NAnt for automating C# builds, one must choose which NAnt task to use to perform the compile.

    One option is the csc task, which invokes the c# compiler via the command line. Another option is the solution task, which uses Visual Studio/devenv to compile a solution.

    The solution task at first glance appears to be the more useful; after all, it respects all project properties such as optimization flags and compilation symbols. However, it has two critical flaws.

    The first flaw becomes an issue in multi-user development environments; the settings of the solution must be synchronized in the repository across each development workstation/user. On the surface, this issue doesn’t seem to be very large. However, in practice, its all too common for users to check in changes to the solution that eventually cause the builds to fail. I want my builds to fail because of compile errors or failed unit tests; not because some one accidentally changed an assembly reference.

    The other issue is that the solution task requires visual studio to be present(and properly configured) on the build environment. Although the size of my company’s software budget does not directly effect my salary, I see no reason to purchase a separate license just so I can automate my builds.

    It is chiefly because of these two issues(and other, much more minor issues) that I choose to use the csc task. The main tradeoff is the fact that I must explicitly script the inclusion of source code, resources, and project references. That’s a tradeoff I eagerly welcome. However, there remains another tradeoff: project configurations.

    Suppose you have a simple target defined as below:

    
    <target name="build">
      <csc target="library" output="assembly.dll">
         ...
         ...
      </csc>
    </target>
    

    How does one go about supporting separate configurations, or defining conditional compilation symbols or to perform optimization?

    I’ve settled on a technique that has served me well for a couple of years now. At the root of my nant script, I define a property named build.release, like so:

    
    <property name="build.release" value="false" unless="${property::exists('build.release')}" />
    

    This property is present to indicate that a release build should be performed. The property defaults to the value “false”, unless it has already been defined(such as in a calling script, or via the command line).

    I then use this property within the call to the csc task:

    
    <csc target="library" output="assembly.dll" debug="${not build.release}"
      optimize="${build.release}">
      ...
      ...
    </csc>
    

    The use of this property allows me to control both the optimize argument and the debug argument. If debug.release is true, then the assembly will be optimized, and the debug argument will be set to false. If debug.release is instead false, then the assembly will not be optimized, and the debug argument will be set to true. In the latter case, according to the NAnt docs, both the DEBUG and TRACE symbols will be defined.

    If your project requires alternate compile symbols, one can always use a similar method to conditionally define them and pass them to the csc task(via it’s define argument).

    Unfortunately, there’s one glaring flaw; fortunately, this flaw has not affected me yet in my 2+ years of this approach. The flaw is the fact that only two configurations are supported. It’s either a debug build, or a release build.

    Great - so you now have one set of nant code that uses the csc task that can emit either debug or release builds. How does one take advantage?

    One method is to change the initialized value of the debug.release property, either directly in the script or its calling script. Another method is to pass the value via the command line, via the line:
    NAnt [buildfile] -D:debug.release=true

    But what happens if you have the build triggered by a continuous integration environment, like CruiseControl.NET? That’s the topic of another post altogether.

    July 16, 2007

    Unit testing WinForms forms, Model/View/Controller, and events

    Filed under: Unit Testing, c#, dotnet — treyhutcheson @ 8:59 am

    I was recently developing a new dialog for one of our integration layers using TDD. When I do this, I use the MVC pattern. I create an interface that defines the operations available on the view, and I create a separate controller class that accepts a view in it’s constructor. I make the test fixture implement the view interface, and the actual view implementation(form) is nothing more than wiring up the interface’s implementation to it’s own controls and/or events. It’s pretty simple, and has served me well in the past.

    While developing this particular view/controller, I tried a new twist. I exposed a series of events on the interface, and implemented listeners on the controller. An example would be the OnOk event, which is fired when the user clicks the Ok button on the physical dialog. The view implementation handles it’s own internal ok button click event by raising the interface’s OnOk event.

    Part of the contract with the controller is that each public event on the view will be handled, and automatically wired during the construction of the controller. I wanted to force this behavior through a unit test, and came up with the following method:

    void AssertIsListening( object instance , MulticastDelegate @event , string eventName );

    This method takes an object instance on which the assertions will be performed, the actual event to check, and the name of the event. It is called in the form AssertIsListening( controller , OnOk , "OnOk" );

    The code works by accepting a multicast delegate. The MulticastDelegate class defines a method named Delegate[] GetInvocationList();. Internally, the AssertIsListening method calls MulticastDelegate.GetInvocationList, and loops through the resulting array of delegates. During each iteration, it checks to see if the delegate’s Target member is the same as the instance method argument( instance.Equals( delegate.Target ); ). If the instance argument is not found in the list of delegate targets, the assertion fails.

    Here’s the full code for the method(please forgive the formatting):

    void AssertIsListening( object instance , MulticastDelegate @event , string eventName )
    {
    bool found = true;

    Assert.IsNotNull( @event , “Event {0} is null” , eventName );
    foreach( Delegate d in @event.GetInvocationList() )
    {
    if( instance.Equals( d.Target ) )
    {
    found = true;
    break;
    }
    } //END delegate target loop

    Assert.IsTrue( found , “Object not found in invocation list of event {0}” , eventName );
    } //END AssertIsListening method

    Using the above method, I can make sure that the controller handles each event defined on the view’s interface, and that the event handlers are added automatically from the controller’s constructor. It works well. However, when I got down to testing, I ran into an unforeseen problem: stale object references.

    It’s more of a peculiarity to my test fixture than anything else, but it did expose a potential problem in the future. My test fixture has something on the order of 40 test cases. Each test case begins by calling the constructor of the controller. That means when all tests in the fixture have completed, about 40 instances of the controller have been created, which means that the mock view(the test fixture) will have 40 event listeners for each of it’s events. That means that when I fire an event from the mock view, more than once controller instance will handle the event.

    When I first encountered this problem, it didn’t make any sense. After all, the controller that was being created in each test case was local to that test case. It shouldn’t have remained alive. So I threw in a GC.Collect() inside the fixture’s TearDown method to force a collection after each test case. After that I still had old controller instances handling each event. It had me completely stumped. The old controller instances should have zero references and should have been collected. What was going on?

    Then I realized the problem: the test fixture instance was being reused for all test cases(NUnit 2.2). The controller automatically wires up the view’s events during construction, so each controller instance, even though local to each test case, was still “wired in” to the test fixture instance. That means that even after a garbage collection, the stale controller instances were still visible through the object reference walking performed by the GC. I solved this issue by implementing IDisposable on the controller and having the controller remove the event handlers during IDispose.Dispose();.

    It seems obvious in retrospect, but it causes me concern for the future. In standard WinForms development, most event handlers are implemented within the form that exposes the events, so the delegate target object reference is a circular reference to the form itself, which the garbage collector will take care of without problem. But, there is a possibility that outside of WinForms, some component will expose an event, and the object that handles the event will never be reclaimed if the event sink is long-lived. Just a heads-up.

    July 12, 2007

    Lesson Learned: Custom NAnt Tasks

    Filed under: NAnt, c#, dotnet — treyhutcheson @ 7:46 pm

    I’ve been meaning to post this for a while. A few months ago I ran across an issue that was a little difficult to debug. I had written a custom NAnt task that performed some logical work on a file. This task was used in multiple scripts that comprised one larger build process: one master script launched more specialized scripts via the nant task.

    Well, this issue boils down to pathing. All invocations of the custom task used relative paths; relative to the script itself. So paths in the root script were relative to the root, and paths in child scripts were relative to the subdirectory containing said child script. During this process, I encountered all kinds of pathing issues: mainly file not found exceptions. It turned out that the assembly containing the custom task was loaded by the root script, so it’s current directory was that of the root script. Subsequent invocations of the task, using paths relative to the child scripts, did not resolve to correct physical paths.

    The solution is to resolve all file paths to that of the nant script invoking the task. But of course, if the path is already an absolute path, don’t do this. You can determine if the path is an absolute path via the Path.IsPathRooted() method. If it’s not rooted, then you can create a new path resolved to the current script’s directory via Path.Combine( project.BaseDirectory , [path] );.

    Hope this helps somebody out there.

    Older Posts »

    Blog at WordPress.com.