Techkriti

August 30, 2008

Using Groovy with Hudson to send rich text email

Filed under: Java — Chetan @ 7:27 pm

Recently I migrated from CruiseControl to Hudson as our CI build server. One thing I found lacking in Hudson was to send rich text email containing information regarding changelist details, test result etc. The email extender plugin do provide few variables to be inserted into the mail but that is very limiting.So I modified the plugin to send HTML email which were generated using Groovy‘s SimpleTemplate.

In the template the AbstractBuild object was exposed as a binding. Using that one can compose email containing some changelist info, test stats etc. Further the Groovy template can be used to write email content in any form, look and feel as one wants. This post would explain how to write a simple groovy script to display build related information

A patch has been submitted to Hudson as REF2175. In this post I would try to explain how this feature can be put to use.

Plugin Configuration

Email-Ext configuration

Email-Ext configuration

The updated plugin now provides new fields for adding the mail template. In the plugin template you can add any groovy based script which would then be passed to the groovy template engine. Also you can enable weather the email is of type text/html or text/plain.

All the variables provided by the email-ext plugin earlier are still available for use as bindings

Mail Content

For the mail content we would use the Groovy’s SimpleTemplate. The complete template can be accessed from here. The email generated has following main sections.

  • General Information
  • Committed changelist
  • Test Result summary
  • Build logs

Lets see few of these in details.

Collecting Changelist

In the template we first collect the changelist information from the build object. For more details refer to the properties exposed by the AbstractBuild. So here we collect all the changelists from lastSuccesfulBuild. Later we would use this to display the changelist contents in details

<%
def lastSuccesfulBuild = build.previousNotFailedBuild
def failed = build.result != hudson.model.Result.SUCCESS

def currResult = build.result
def prevResult = build.previousBuild?.result ?: null

def consecutiveSuccess = currResult == hudson.model.Result.SUCCESS && prevResult == hudson.model.Result.SUCCESS

def builds = []
def changes = []
def count = 0

if(consecutiveSuccess){
   builds << build
   def changeItems = build.changeSet.items
   count += changeItems.length
   changes += changeItems as List
}else{
    while(lastSuccesfulBuild){
	builds << lastSuccesfulBuild
	def changeItems = lastSuccesfulBuild.changeSet.items
	count += changeItems.length
	changes += changeItems as List
    	lastSuccesfulBuild = lastSuccesfulBuild.nextBuild
    }
}
%>

Displaying the Changelist details

Now we would iterate over the changes to display the details. The groovy code is highlighted below. In my case the SCM repository is Perforce. So the changelist object is PerforceChangeLogSet and hence the properties accessed are exposed by it

 <tr>
    <td><b>Modifications since last  successful build:&nbsp; (${count}) </b><hr size="2" width="100%" align="center"/></td>
   </tr>
   <tr>
   	<td>
    	<table width="100%" border="0">
        <% changes.each { item ->
	    def cl = item.change
	%>
          <tr>
            <td style="font-size:10pt; font-family:Arial, Helvetica, sans-serif">
              <a href="http://perforceserver/cgi-bin/p4describe.pl?@${cl.changeNumber}">${cl.changeNumber}</a>
                by ${cl.user}@${cl.workspace} on ${cl.date}</td>
          </tr>
          <tr>
            <td><pre style="font-size:8pt; font-family:Arial, Helvetica, sans-serif">${cl.description}</pre></td>
          </tr>
          <tr>
            <td style="font-size:10pt; font-family:Arial, Helvetica, sans-serif">
             <span style="font-size:10pt; font-family:Arial, Helvetica, sans-serif; "> Affected files:</span>
            	<ul style="font-size:8pt; font-family:Arial, Helvetica, sans-serif">
                <% cl.files.each{ fileEntry -> %>
                  <li>[<a href="http://perforceserver/cgi-bin/p4browse.pl?@diff+${fileEntry.filename}+${fileEntry.revision}+{@action}">Diff</a>]
                  [<a href="http://perforceserver/cgi-bin/p4browse.pl?@filelog+${fileEntry.filename}">History</a>]
                  ${fileEntry.action} ${fileEntry.filename} (${fileEntry.revision})
                  </li>
             	<% } %>
      		   </ul>
               <hr size="1" width="80%" align="left" />
            </td>
          </tr>
         <% } %>
	    </table>
    </td>
   </tr>

This results in following output

Test Result display

I had a requirement to depict the test results in the mail. The result should display the testcases which have started failing seperately from the previous failing test.(I know all test should pass but in reality few test continue failing due to some reasons or other). So here we iterate over current test results and previous test results to determine the delta. Also we collect some data to create a link for the failing test

Segregating the test results in such a way brings the required attention to the test cases which had started failing

  <%
	if(build.testResultAction) {
	    def rootUrl = hudson.model.Hudson.instance.rootUrl
		def testResult = build.testResultAction
		def jobName = build.parent.name

		def lastBuildSuccessRate = String.format("%.2f",(testResult.totalCount-testResult.result.failCount)*100f/(testResult.totalCount))
		def lastBuildDuration = String.format("%.2f",(testResult.result.duration ))

		def startedPassing = []
		def startedFailing = []
		def failing = []

		def previousFailedTestCases = new HashSet()
		def currentFailedTestCase = new HashSet()

		if(build.previousBuild?.testResultAction){
			build.previousBuild.testResultAction.failedTests.each {
				previousFailedTestCases << it.simpleName +"." + it.safeName
			}
		}

		testResult.failedTests.each{tr ->
			  def packageName = tr.packageName
			  def className = tr.simpleName
			  def testName = tr.safeName
			  def displayName = className+"."+testName

			  currentFailedTestCase << displayName
			  def url = "${rootUrl}job/$jobName/lastBuild/testReport/$packageName/$className/$testName"
			  if(tr.age == 1){
				startedFailing << [displayName:displayName,url:url,age:1]
			  } else{
				failing << [displayName:displayName,url:url,age:tr.age]
			  }
		}

		startedPassing = previousFailedTestCases - currentFailedTestCase

		startedFailing = startedFailing.sort {it.displayName}
		failing = failing.sort {it.displayName}
		startedPassing = startedPassing.sort()
   %>

This data is then later displayed in list form as shown below

Conclusion

Using Groovy for generating the email content provides considerable flexibility. It exposes the Hudon data model (which is quite neatly designed) and makes it easy to extract information from it. Further it reduces the requirements to modify the plugin code considerably.

I find it more convenient over the xsl based approach used by CruiseControl. Below is a snapshot of the complete mail send to the users.

Update

I have uploaded the compile hpi file here. Just download it and then add it to your Hudson setup at Hudson -> Manage Hudson -> Manage Plugins -> Advanced

Advertisements

June 28, 2007

Maven2 Hibernate Plugin and Spring

Filed under: Java — Chetan @ 2:43 pm

Continuing my explorations on Maven I had a requirement to generate the database schema using Hibernate tool feature. And maven had a plugin readymade for this. The maven plugin expects the hibernate configuration through the config file but we were using Spring’s LocalSessionFactoryBean to maintain the Hibernate configuration.

Stuck with that I downloaded the source which is scattered over multiple sub modules. Browsing through that I saw few annotations like
@component role=”org.codehaus.mojo.hibernate3.configuration.ComponentConfiguration” over some attributes. Now that was something new and I had no idea what it was meant for. Searching about it I found ( should have known) that Maven internally uses Plexus which is an IOC container similar to Spring.

That gave me the only hope of using the plugin without modifying it and not wasting much time. The plexus container works on the concept of Role. Let me explain that a bit with maven plugin. The plugin uses an interface ComponentConfiguration for retrieval of the Hibernate configuration from various sources like config file,annotation etc. Thus in theory I can plugin my own implementation which gets the config from Spring container. Now the question was how to inject it.

The Plexus container works on the concept of Role. Have a look at this 5 min tutorial at there site. Here is the interface for the ComponentConfiguration

public interface ComponentConfiguration {
    String ROLE = ComponentConfiguration.class.getName();

    Configuration getConfiguration( ExporterMojo exporterMojo )
        throws MojoExecutionException;

    String getName();
}

The ROLE field acts as a key for the container.The mapping of implementation to the role is done through an xml located at META-INF/plexus/components.xml inside the jar. So here was my mapping

<component-set>
  <components>
    <component>
      <role>org.codehaus.mojo.hibernate3.configuration.ComponentConfiguration</role>
      <role-hint>springconfiguration</role-hint>
      <implementation>example.hibernatetool.SpringComponentConfiguration</implementation>
    </component>
  </components>
</component-set>

Next comes the questions how the plugin would pick a particular implementation. Here the role-hint comes. The HibernateExporterMojo which all the mojo’s extend has a field

    /**
     * @component role="org.codehaus.mojo.hibernate3.configuration.ComponentConfiguration"
     * @noinspection MismatchedQueryAndUpdateOfCollection
     */
    private List componentConfigurations = new ArrayList();

So when Maven creates the plugin class then Plexus populates componentConfigurations with the implementations of ComponentConfiguration. And thus our implementation also gets injected.Now only task left is the final wiring of all these things through pom.xml

     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>hibernate3-maven-plugin</artifactId>
        <version>2.0-SNAPSHOT</version>
        <configuration>
            <componentProperties>
                  <implementation>springconfiguration</implementation>
                  <console>false</console>
              <outputfilename>schema.sql</outputfilename>
              <appcontextlocation>
                  classpath:spring-context.xml
                  </appcontextlocation>
            </componentProperties>
        </configuration>
        <dependencies>
           <dependency>
            <groupId>example.plugin</groupId>
              <artifactId>hibernate-support</artifactId>
              <version>1.0-SNAPSHOT</version>
            </dependency>
           <dependency>
               <groupId>org.hibernate</groupId>
               <artifactId>hibernate</artifactId>
               <version>3.2.1.ga</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-annotations</artifactId>
                <version>3.2.1.ga</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.0.0-beta</version>
            </dependency>
        </dependencies>
      </plugin>

Here though implementation property we tell the plugin to use my implementation for getting Configuration. Then through dependencies I add my class’s jar to plugin classpath. The hibernate jar was required as plugin was picking some wrong jar and causing problems.Thats all that was required.

Execute the plugin through mvn hibernate3:hbm2ddl and get the ddl script generated for the schema.

Create a free website or blog at WordPress.com.