Tuesday, April 7, 2009

Groovy CruiseControl Helper - part 1 RSS and Regex

I've been wasting entirely too much time deploying builds lately as our CruiseControl instance produces upwards of 35 builds, of which I only care about 4.   Not to mention that downloading each ear file is at least 3 page 'clicks'.   Ignoring for now that CruiseControl would happily deploy my ears for me (team's CruiseControl guru is overbooked right now),  I've started building a Groovy utility to leverage the RSS feeds produced by CruiseControl in order to run an automatic pull of the latest builds for my 4 target ears.

I started tinkering with examples found in "Groovy In Action" and in Netbeans, but moved over to Eclipse 3.4.1 when things were ready to implement as that's the standard at work.   Netbeans was more 'Groovyish' and very easy to start scripting with, whereas Eclipse is more 'Javaish' expecting you to produce classes rather than 'scripts'

In the end I built one Class  to handle  the RSS and information parsing  and one Script to act as my main and run my new process.

My Class simply opens an RSS feed, pulls data from the top item and parses out the stuff I need to find my assets for deployment.

/*
   For a specific RSS feed and project.   Get the key data for the latest build
*/
public class GetRss {
   private myBase;
   private myProject;
   private buildNo;
   private assetNo;

   GetRss (String base,  String project) {
     myBase = base;
     myProject = project;
  }

   void doRss()
   {
       //open the Rss Feed URL,  parse date into XML Document
       def items = new XmlParser().parse(myBase + myProject).channel[0].item
       //GPath notation to get to elements of interest
       // title tells me if this build passed or failed
       println items[0].title.text()
       //The link is to build HTML - I want only some of this info
       parseLink( items[0].link.text())
    }
    
/*
   parseLink uses regular expressions to pull out the two items I need from the link
   Build No is what I will use to identify the build to QA  ##.##.##.##  
   Asset No is what CruiseControl uses to uniquely identify build results - 14 digit number
*/
      void parseLink(String link)
     {
            //Link contains build #  - what the build will be know by in the real world
            // build number is pattern of 4 sets of digits separated by .
            def buildMatcher = ( link =~ /-\d+[.]\d+[.]\d+[.]\d+/ )
            buildNo = matcher[0]
            //link also contains CruiseControl build ID - 14 digit number
            def matcher = ( link =~ /\d{14}/ )
            assetNo = matcher[0]

      }

Part 1 - merely generates URLs for where each ear file is stored by the CruiseControl process.   So instead of many page clicks to download.  I can run the following script and get 4 URLs ready to download the assets from.   Stay tuned for Part2 where I will use AntBuilder and Groovy to automatically FTP these assets to where I need to stage them...

//The common paths set by cruisecontrol
def cruiseServer = 'http://mybuild.server.com:8081'
def allRSS = "$cruiseServer/cruisecontrol/rss/"
def allAsset = "$cruiseServer/crusiecontrol/artifacts"

//Read RSS for Project1 and get key data
GetRss uiservices = new GetRss( allRss, "Project1-BuildWar")
uiservices.doRss()

//Print the line with build No and URL to deployment asset
println " $uiservices.buildNo $allAsset$uiservices.myProject/$uiservices.assetNo/project1.war"

//... Repeated above for each additional project
The script outputs two lines, first the item title with time of build and status.  Then a second line with the build No and a link that I can cut and paste into browser to initiate the download of the deployment asset.

Example output for first project

Tue Apr 07 20:41:47 EDT 2009, passed   
-10.12.1.191  http://mybuild.server.com:8081/cruisecontrol/artifacts/Project1-BuildWar/20090403180111.project1.war


As mentioned, in part 2 I will develop the code to directly download the build assets using the URLs generated in part 1.   

Friday, February 27, 2009

Okay,  why I really want to learn Groovy is that I need to adapt Unit Testing to a large, ancient legacy java application.   With no existing tests, and more 'spaghetti' than will be eaten in Italy this year,  I'm trying to inject mocks and stubs without having to refactor every class.

I've run the sample found at http://docs.codehaus.org/display/GROOVY/Using+the+Delegating+Meta+Class  where the String class behavior is changed during a script run.   However, the change is not replicated when the string.toString() method is called inside of a Java class (triggered from the Groovy Script).

I figure I'm missing a crucial understanding of the Groovy vs Java Classloader...  Or is that all real coders always design objects with setDepenedency() methods that allow for easy code injection?

Thursday, February 26, 2009

Regex File Search with Groovy

I needed something interesting to get me going.  And then I moved to a new team at work, losing access to my nice tool rich linux account, leaving me with only a shiny new Dell Windows laptop and a need to quickly hunt through a large, legacy codebase.   Hooray - Groovy to the rescue!

The first pass at this script took me about 2 hours from Groovy Console install to working code.  This cleaned up version took me maybe another 2 of tweaking the regex and collapsing code down into more compact closures.    For reference I leaned heavily on Google and the working with files and I/O section from "Groovy in Action".

(Prerequisite note on starting point for true beginners - download the installer from groovy.codehaus.org,  following code was developed and run from the GroovyConsole
My apologies for any syntax bugs introduced in moving code into this format)

Quick Summary of lessons in developing this code:
  • If you don't call flush on writing to a file, file most often empty when script exits.
  • Explicitly calling java.util.Pattern does not produce the same matching behavior as using the Groovy notation
  • I'm still a little confused on where is safe to use single or double quotes, I got lots of errors about parsing the script.  Seemed like comment indicators don't completely insulate against the console recognizing quote marks.
  • I'm not sure how much patience I'll have in the future for the java.io package!

//*************
//Walk through a set of files
//collects information about a target string into an output file.
//*******************

// no need to define type for variables, just use generic def keyword
def outputfile = new File("myresultsfile.out").newWriter()

//This is the class invented for ant 
//spiders through directories to get list of files to search. 
//list of prefixes identifies specific file types of interest.
FileNameFinder dirList = new FileNameFinder();
List sourcefiles = dirList.getFileNames("mysrcdirectory/", "**/*.jsp, **/*.java, **/*.js, **/*.javascript, **/*.properties, **/*.xml, **/*.conf, **/*.props")

//defining a holder for results.  I want unique so using a set
Collection fileList = new HashSet();

//define a simple function to control where results are written. 
//with closures the 'method' is simply defined as another type.  
//Default input variable is it
def collectResults = {
  println it
   outputfile.writeLine(it)
}

//function takes three inputs filename, linenumber, and line
//for the line that holds the pattern
//write out the information to the output file
//save the filename in a set to generate a list of target files

def processfile = {
//defining specific function inputs instead of using default it
   srcfile, srcline, src ->   //newbies note the arrow signature it's important!

//find all text matches in the line, 
//no need to mess with java.util classes
// Groovy's added simple syntax to generate a matcher (like Perl)
   def finder = ( src =~ /.*targetstring*/ )
   if (finder.matches())
   {
     //write file and line number and matched results
       fileList.add(srcfile)
      //another favorite script feature available in Groovy
      //just use token syntax to generate mix of variable and static output
       collectResults( "${srcfile}^${srcline}^${src}")
    }
    else
  {
    //easy to debug - print information to sysout without all the typing.
    println 'did not find ' + finder.pattern()
   }
}

//Now for the meat of the script. 
//Notice the easy for iteration syntax,  
//define a list or collection and a variable to hold each reference (again see perl)

for ( sourceFil in sourcefiles){
//read file in 
  File fileHandle = new File(sourceFil)
   Reader openFile = fileHandle.newReader()
   //for each line - use the Groovy defined easy file line reader method.
   // this method takes a closure and run it each line of a file,
   // notice 'it' is the active line of file
   openFile.eachLine{ processfile(sourceFil, openFile.getLineNumber(), it)}
}

//when done processing files flush output file to ensure capture of text
outputfile.flush()


//Also check if want to write out list of unique file names captured
if (fileList.size() > 0)
{
    def chgFiles = new File("filesofinterest.out").newWriter()
    //First line to tell me how many files were identified to contain the target pattern
    chgFiles.writeLine(fileList.size().toString())

//again using a nifty Groovy method added to every list or collection type 
//passing it a closure to execute for each element in the list
    fileList.each{ 
       chgFiles.writeLine(it)
       println(it)
     }
    //ensuring the output gets written
    chgFiles.flush()
}

How I got started with Groovy (and blogging about it)

My first exposure to Groovy happened at my local Java Users Group (www.ajug.org).  We had a demo over a year ago by none other than Jeff Brown.   I hate to admit that while I was completely blown away by that talk,  I procrastinated actually getting my hands dirty with learning Groovy.   

My first roadblock was being a complete novice to any kind of dynamic language.   My extremely conservative corporate employer had up to now let the Javascript and Web 2.o revolution pass them by.  I had written a few Perl scripts, but never for anything except as a workaround to some of the limitations to shell scripting.    Groovy code doesn't make  whole lotta sense until you can grok closures, and the lightweight syntax.  Thankfully, a coworker pointed me to YUI Theater where Douglas Crockford explains Javascript as a real language.   Now at least I understand the concepts of a robust dynamic language.  

That of course leaves only the small problem of learning to use the language properly.   I find book examples are never all that informative, it doesn't take much to wander off the reservation and end up really lost.  I've recently been advised that lots of free expert advice is available, if only you post to a blog.  Blame Jared Richardson for daring me to post a blog and share my dabblings with the universe at large.