Groovy Scriptdef to detect properties with trailing spaces

One potential source of errors in a project is the presence of undesired terminating spaces on properties loaded by java.util.Properties. Since property files are also used to deploy or configure applications, it is possible for these sources of errors to disrupt deployed services.

Alternatives
There are of course many options to prevent this from happening, such as:
1. Property file or general editors that can detect or show “spaces”.
2. Another is to create property file loaders that can ‘trim’ spaces.
3. And, yet another is that configuration properties are part of the resources that a management layer monitors as part of an autonomic processing.
4. Perhaps an optimal and most immediate approach is to just have actual tests triggered on any changes, and, where it matters, properties are validated according to specified rules.

The simple approach
An Ant Scriptdef definition implemented in the Groovy language. This can be easily used in the build and deployment process. It can also be used at the deployment location or remotely to check deployed property files.

In listing 1, an Ant script shows the definition of the scriptdef and its use.

Listing 1, Snippet of Ant script

<scriptdef name="blankpropertycheck" 
      language="Groovy" 
      classpathref="libs" 
      src="TrailingSpacePropertiesCheck.groovy">		
		
             <attribute name="failonerror"/>
             <attribute name="property"/>
             <attribute name="verbose"/>
		
             <element name="fileset" type="fileset"/>		
</scriptdef>

<target name="init"  description="---initialize build">
    <!-- use the scriptdef -->	
    <blankpropertycheck
           failonerror="true"
           property="anyFound">		

        <fileset dir=".">
             <include name="**/*.properties"/>
        </fileset>		
			
    </blankpropertycheck>
		
    <echo>Properties with trailing spaces:</echo>
    <echo>${anyFound}</echo>
		
</target>

The new task definition specification:

Attributes
failonerror optional
verbose optional, one of verbose or property
property optional, one of verbose or property
Elements
fileset required

Now we create two example property files in the directory (not shown here) and run the Ant script, with results shown in listing 2. This output was produced with a debug variable set true, to show the data binding in the scriptdef.

Listing 2, sample run

C:tempantblanksCheck&gt;ant
Buildfile: C:tempantblanksCheckbuild.xml

init:
[blankpropertycheck] ****************************************************************
[blankpropertycheck] failOnError='true'
[blankpropertycheck] propToSave='anyFound'
[blankpropertycheck] verbose='null'
[blankpropertycheck] fileset='abc.properties;liblog4j.properties;libxyz.properties'
[blankpropertycheck] ****************************************************************
     [echo] Properties with trailing spaces:
     [echo] C:tempantblanksCheckabc.properties
     [echo]     four=[4    ]
     [echo]     twoWithSpaces=[2  ]
     [echo] C:tempantblanksChecklibxyz.properties
     [echo]     badwiththreespace=[m   ]

BUILD SUCCESSFUL
Total time: 1 second

The Groovy script is shown in listing 3 below. As implemented, the failonerror is for script or variable binding errors. Since properties could by design have trailing spaces, this is not considered an error. It is easy to change the script to behave differently, of course.

The scripts also serves as an example (to myself) of:

  • regular expression use
  • dynamic object to boolean conditionals
  • accessing object properties via a map
  • reusing closures as actions

Listing 3

/*
    File: TrailingSpacePropertiesCheck.groovy
    Author: josef betancourt
*/

import org.apache.tools.ant.*
import org.apache.tools.ant.types.*

boolean debug = true

failOnError = attributes.get("failonerror")
propToSave = attributes.get("property")
verbose = attributes.get('verbose')
fileset = elements.get('fileset')[0] as FileSet

if(debug){
    band = '*' * 72
    println(band)
    ['failOnError','propToSave','verbose','fileset'].each{
        println "$it='${this[it]}'"
    }
    println(band)
}

// Do we have all essential attributes and elements?
def msg =''
msg =  !fileset ? "'fileset' not found;" : ''
msg += !(verbose || propToSave) ?
         "both 'verbose' and 'propToSave' not found" : '';

if(msg){
    if(failOnError){
        throw new BuildException(msg)
    }
    
    println msg
    return    
}


pattern =  ~/(s+)$/

/** Count any trailing spaces in string  */
int countTrailingSpaces(aString){
    def matcher = pattern.matcher(aString)
    def result = matcher.find()
    
    if(result){
        def blanks = matcher[0][1]
        return blanks.size()
    }
    
    return 0
}


def props = new Properties()
def count
// map of  file:[list of potential properties in error]
def results = [:]

// analyize each file in fileset
fileset.each{
    File file = it.getFile()    
    props.load(file.newReader());
    def list = []
    
    props.each{ entry ->
        count = countTrailingSpaces(entry.value)
        if(count){
            list.push(entry)
        }
    }
    
    if(list){
        results[file.getPath()] = list
    }
    
    props.clear()

} // end each file

def action // a closure
def resString = ""

if(!results){
    return
}

if(verbose){
    action = { x -> println x}
}else{
    // converts to StringBuilder
    resString <<= ""
    action = {x -> resString << (x + "n") }
}
    
results.each{
    action("${it.key}")
    it.value.each{
        action("t${it.key}=[${it.value}]")
    }
}
    
if(!verbose){
    project.setNewProperty(propToSave,resString.toString())
}

// end of file TrailingBlankPropertiesCheck.groovy

Properties
Should any properties in configuration or data files have trailing spaces? Seems like having one of those “kick me” signs on your back. If one could use visible terminators like a quote or brace, but usually a Java based properties file will not have terminators, afaik.

Summary
Shown was a simple method of checking property files for entries with trailing spaces on values.

Further Reading

  • The Groovy Truth” Groovy has special rules for coercing non-boolean objects to a boolean value. Using this language feature makes coding much more productive.

Leave a comment