Monday, December 21, 2009

Ant task ported to Groovy - Part 2

Welcome back if you read part 1 of this posting.  If you haven't read part 1, you may want to take a quick look at that before reading this part of the posting.  This posting (part 2) will show the following changes or ports from Java to Groovy for the validator ant task.

  • CliBuilder for command line interface
  • ValidatorDescriptor class
  • Consuming the properties file
Switch from Ant to a Groovy task
As I mentioned in part 1, this port will not be an exact port because I am creating a groovy script rather than an Ant/Gant task - partly because I haven't played with Gant yet!  Some of the existing Ant task code is shown below. For those familiar with creating Ant tasks, you will notice the class extends Task which is an abstract class.   The code overrides the execute() method.   The validator task checks the input and output parameters and then calls the validate() method to validate the files.

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.PatternSyntaxException;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class Validator extends Task {

    private static final String DEFAULT_EXCLUDE = "";
    
    // constants used to create the property file keys 
    // --> extension..regex or extension..description 
    private static final String REGEX       = ".regex";
    private static final String DESCRIPTION = ".description";
    private static final String EXCLUDE     = ".exclude";
    
    private String extension;
    private String directory;
    private String propertyFile;
    private String reportFile;
    
    private int fileCnt;
    private int errorCnt;
    private List descriptors;
    private Properties props;
    private Map errorMap;

    /**
     * Default constructor
     */
    public Validator() {
        props = new Properties();
        descriptors = new ArrayList();
        errorMap = new HashMap();
        errorCnt = 0;
        fileCnt = 0;
    }

    /**
     * Sets the extension for the task
     * @param extension extension of the files to be validated
     */
    public void setExtension(String extension) {
        this.extension = extension;
    }
    
    /**
     * Sets the root directory to be processed.  This directory
     * and all of its subdirectories will be processed.
     * @param directory directory to be processed.  
     */
    public void setDirectory(String directory) {
        this.directory = directory;
    }
    
    /**
     * Sets the root directory to be processed.  This directory
     * and all of its subdirectories will be processed.
     * @param directory directory to be processed.  
     */
    public void setPropertyFile(String fileName) {
        this.propertyFile = fileName;
    }

    /**
     * Set the report file to be created as output
     * @param reportFile name of the html file to be
     *   generated to show any errors
     */    
    public void setReportFile(String reportFile) {
  this.reportFile = reportFile;
 }
    
 /**
     * Creates an xml file from the bugs file
     */
    public void execute() throws BuildException {
        checkParameters();
        try {
            validate();
        } catch (BuildException e) {
            throw e;
        }
    }

    /**
     * Check that all required attributes have been set
     * @throws BuildException if any of the required attributes
     *      have not been set.
     */
    private void checkParameters() {
           if (extension == null || extension.length() == 0  
                ||   directory == null || directory.length() == 0 
                ||   reportFile == null || reportFile.length() == 0 
                || propertyFile == null || propertyFile.length() == 0) 
               throw new BuildException("Extension, directory, reportFile and property file attributes "
                      + "must be defined for task <" + getTaskName() + "/>" );
    }

    private void validate() {
        File dir = new File(directory);
        log("Processing file extension : "+extension+"\n");
        
        try {
            props.load(new FileInputStream(propertyFile));
                      
            setDescriptors();
            visitAllDirsAndFiles(dir);
            
            writeReport();
            
            log("Validator processed "+fileCnt+ " files and found "+errorCnt+" possible errors!");
            
        } catch (Exception e) {
            log(e.toString());
        }
    }
   private void writeReport() {
     try {
  FileWriter writer = new FileWriter(reportFile);
  writeHeaders(writer);
  writeContents(writer);
  writeTrailer(writer);
  writer.close();
 } catch (IOException e) {
  throw new BuildException(e);
 }
   }

   // some code omitted here - but shown later!!!
}

Groovy replacement using CliBuilder
While not a whole lot shorter than the Ant equivalent, it is short, concise and a good example of CliBuilder usage.

def cli = new CliBuilder (usage: 'groovy Validator -e extension -p propertyFile -d directory -r reportFile')

cli.h(longOpt: 'help', 'usage information')
cli.e(longOpt:'extension',  args:1, required:true, 'file extension to be validated')
cli.p(longOpt:'prop',       args:1, required:true, 'property file containing regular expresssion')
cli.d(longOpt:'directory',  args:1, required:true, 'base directory to start file search')
cli.r(longOpt:'reportFile', args:1, required:true, 'output file for validation report')

def opt = cli.parse(args)
if (!opt) return
if (opt.h) cli.usage()

println "start processing $opt.e files using $opt.p from $opt.d and output to $opt.r"

RegExDescriptor becomes ValidatorDescriptor
First the java...
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * Regular Expression Descriptor used to help
 * validate source files.  
 *
 */
public class RegExDescriptor {

    private String description;
    private Pattern excludePattern;
    private Pattern pattern;

    public RegExDescriptor(String regExString, String description, String exclude) throws PatternSyntaxException {        this.description = description;
        pattern = Pattern.compile(regExString, Pattern.CASE_INSENSITIVE);
        if (exclude != null && exclude.length() > 0)
            excludePattern = Pattern.compile(exclude);
        else
            excludePattern = null;
    }

    public Matcher getMatcher(String line) {
        return pattern.matcher(line);
    }

    public String getDescription() {
        return description;
    }
    
    public boolean excludeFile(String name) {
        if (excludePattern == null) 
            return false;
        Matcher matcher = excludePattern.matcher(name);
        return matcher.find();
    }

    public String getPattern()  {
     return pattern.pattern();
    }
    
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("Validator description=").append(description);
        sb.append(" regEx=").append(pattern.pattern());
        if (excludePattern != null)
            sb.append(" exclude regEx=").append(excludePattern.pattern());
        return sb.toString();
    }
}
Now the Groovy equivalent, which I renamed to ValidatorDescriptor. Like a lot of Java classes ported to Groovy, it is significantly shorter since we don't need to specify the getters and setters. The toString() method is really needed but I used it in testing to display the ValidatorDescriptor classes created from the properties file.
class ValidatorDescriptor {
    String description
    String exclude
    String regex

    String toString() {
       "regEx="+regex + " description=" + description + (exclude ? " exclude("+exclude+")" : "")
    }
}
Consuming the properties file
The next task is to read the properties file and create a list of objects representing each of the errors I am attempting to capture.   If you remember from part 1 of the posting, there can be three properties: regex, description and exclude.  The java processing to read the file and create the list of RegExDescriptors was located in two steps: the first was to read the file into a Properties object and then call the setDescriptors method in the Validator class as part of the main method called by Ant.

The idea is to group the properties together by the extension and the number that is part of the key. Each matching set is used to construct the RegExDescriptor.  The RegExDescriptor creates a Pattern for the regular expression checking for the error and another for the file name exclusion check.  The patterns are compiled at construction time.

props.load(new FileInputStream(propertyFile));
setDescriptors();

// later in Validator
/*
     * Setup the regular expression descriptors 
     * using the extension to determine which properties
     * are to be used when validating the files.
     */
    private void setDescriptors()
    {
        // strip the leading . and add one to end
        String prefix = extension;
        char c = extension.charAt(0);
        if (c == '.')
            prefix = prefix.substring(1);
        prefix = prefix + ".";
        
        int idx = 1;
        boolean done = false;
        
        // build list of validators using the extension to create the property keys
        while(!done) {
            String regexKey = prefix + idx + REGEX;
            if (props.containsKey(regexKey)) {
                String descriptionKey = prefix + idx + DESCRIPTION;
                String excludeKey = prefix + idx + EXCLUDE;
                try {
                    idx++;
                    String exclude = props.getProperty(excludeKey, DEFAULT_EXCLUDE);
                    descriptors.add( new RegExDescriptor(props.getProperty(regexKey),
                                                       props.getProperty(descriptionKey),
                                                       exclude) ); 
                } catch (PatternSyntaxException e) {
                    StringBuffer sb = new StringBuffer(100);
                    sb.append("Exception compiling regular expression key(").append(regexKey);
                    sb.append(") :").append(props.getProperty(regexKey)).append("\n").append(e);
                    sb.append("\nSkipping this expression...\n");
                    log(sb.toString());
                }
            } else {
                done = true;
            }
        }
    }


Now for the Groovy implementation.  It starts out the same, loading a Properties object.  Next, we use the ConfigSlurper class to group and parse the properties.  An empty list to hold the descriptors is defined and then we iterate across the extension (sql in my case) keys and build the new ValidatorDescriptor objects. Notice the use of the GString aware string as part of the key for calling the each closure.
 
Properties properties = new Properties();
  try {
     properties.load(new FileInputStream('validator.properties'));
  } catch (IOException e) {}

  def config = new ConfigSlurper().parse(properties)
  def descriptorList = []
  config."$extension".each {
     descriptorList << new ValidatorDescriptor(it.value)
  }

In the next (and last) posting in this series, we will cover the following:
  • Replace the FileValidator class
  • Using the HTML Builder to generate a report
  • Complete listing of the script and classes

Monday, December 14, 2009

Java Ant task ported to Groovy - Part 1

After reading one of Mr. Haki's recent posts, it reminded me of an Ant task that I had written a couple of years ago that we use in our nightly build. I started thinking about how much shorter and concise the code would be if written in Groovy.  Rather than doing it all in one large posting, I have decided to break it out into 2 or 3 posts and will include all the final Groovy code in the last posting.

The Ant task: Validator
Our product supports several different databases and while can you generally code SQL to the 'lowest common denominator" for a lot of statements, we were encountering problems with our 'upgrade' scripts mostly due to syntax that was more lenient in Oracle and SQLServer but more restrictive in MySQL.   Since the update scripts are not run every day/night, I came up with the idea of using some regular expressions to try and catch these errors as early in the cycle as possible, which is the scheduled nightly build.


    
     




Inputs & Outputs
The validator task takes three input parameters and one output parameters.

Inputs
  1. extension - the extension for the file types to be validated
  2. directory - the directory from which to start checking files
  3. propertyFile- a properties file containing sets of properties for each validation to be performed
Outputs
  1. reportFile - name of the output html file to be created with the results of the validation
Java implementation 
The java implementation of this tasks consists of 3 classes.  The first class, Validator, handles the Ant processing, loads the property file into a list of objects, RegExDescriptors, contains a main loop to visit all directories and files starting at the directory parameter and lastly, writes an output report.   The second class, FileValidator, validates each file line-by-line and records any 'matches' against the regular expressions that represent an error in the SQL runtime syntax.  Lastly, property settings are grouped together into a RegExDescriptor, that contains the description of the error being validated, the regular expression used to 'match' the error and an exclusion regular expression, which is used to exclude some files from some tests. For example, Oracle scripts using the 'nvarchar' data type would be an error so we only want to run that validation against Oracle scripts and not against the SQLServer or MySQL scripts.

Sample property file
This is a sample file containing some of the regular expressions currently being used.   These are subject to change since, in some cases, backward slash had to be escaped in the pattern, like the last example sql.3.regex.

#################################################################
#
# SQL file regular expressions
#
#################################################################

#
# Instances of lines starting with GO
#
sql.1.regex=^GO
sql.1.description=GO statement

#
# Oracle scripts using nvarchar
#
sql.2.regex=nvarchar
sql.2.description=Oracle scripts using nvarchar
sql.2.exclude=SQLServer|MySQL|Store Update

#
# MySQL scripts using ntext
#
sql.3.regex=\\sntext
sql.3.description=MySQL scripts using ntext data type
sql.3.exclude=SQLServer|Oracle


Minor limitation
One minor limitation of this tool is that it only works on a single line at a time.  SQL statements that are longer than a single line are not validated as one SQL statement but simply as lines of text.  This has not been a problem as you can see from the sample validations listed above.  The errors we try to catch are a mixture of syntax and runtime errors we have encountered.

Additionally, this will not be a 'exact' port, meaning, it will not be an Ant/Gant task, but merely a Groovy script, which could then easily be converted for use in those tools.

Port to Groovy 
The first part of the port is easy - replacing all the code that recursively starts at a specified directory and creates a list of files matching the extension provided. First the Java version:

/*
     * Process all files and directories under dir
     */ 
    private void visitAllDirsAndFiles(File dir) {
        process(dir);
    
        if (dir.isDirectory()) {
            FileFilter fileFilter = new FileFilter() {
                public boolean accept(File file) {
                    return file.isDirectory() || file.getName().endsWith(extension);
                } };
            
            File[] children = dir.listFiles(fileFilter);
            for (int i=0; i<children.length; i++) {
                visitAllDirsAndFiles(children[i]);
            }
        }

Now the Groovy version
def sqlFiles = new FileNameFinder().getFileNames(dir', '**/*.sql')
Notice the Ant-like "**/*" prefix for the extension. Groovy does all the work and returns a list of files matching the criteria!

Upcoming 
  • In Part 2
    • Using the CliBuilder for a command line interface (instead of Ant)
    • ValidatorDescriptor as a replacement for RegExDescriptor
    • Consuming the property file
  • In Part 3 
    • Using the HTML Builder to generate a report
    • Complete listing of the script and classes
    • Replace the FileValidator class

Tuesday, October 6, 2009

Data binding for one-to-many association - part 2 (improved)

Improving on the solution...
In a previous posting, I wrote a small sample of how to handle the data binding for a one-to-many association.  While my approach worked, I had a couple nagging issues that I had to resolve.  One, based on the code,  the form field names and the way Groovy builds the map of request parameters,  I had some questions about combining the bindData() method along with also doing objectInstance.properties = properties.  Two, I had this nagging feeling that this could be handle better.

Issue 1
Based on the naming of the form fields that represent objects in the collection, these fields actually appear twice in the params map.  Field addresses[0].street appears in the params map with a key=addresses[0].street and it also appears in a map within the params map with a key=addresses[0].  This 'sub-map' contains all of the fields of the Address object.  My example used this 'sub-map' when calling the bindData() method.  This all works fine.  After completing the iteration across the addresses[x] sub-map entries within the params map, the last statement in the controller was personInstance.properties = params  which then handles the binding for ALL the form fields, including the addresses[0].state field.  This means that I was actually binding the association data twice!  Using the debugger in Eclispe, I was easily able to verify this.

Below is the output of printing the contents of the params object after posting to the PersonController.  The fifth line shows an example of the 'sub-maps' that I mention.
[
addresses[2].city:Arlington, 
lastName:Miller, 
addresses[0].city:Flower Mound, 
addresses[1].zip:66666, 
addresses[0]:[zip:75022, street:1 Main Street, city:Flower Mound], 
addresses[1]:[zip:66666, street:99 Palm Beach Circle, city:Palm Beach], 
addresses[0].zip:75022, 
addresses[2]:[zip:75066,street:1234 Cowboy Way, city:Arlington ], 
addresses[2].zip:75066, 
addrCount:3, 
addresses[0].street:1 Main Street, 
action:save, 
addresses[2].street:1234 Cowboy Way, 
controller:person,
addresses[1].city:Palm Beach,
firstName:Mike, 
addresses[1].street:99 Palm Beach Circle
] 

Issue 2
That nagging feeling just wouldn't go away.  I believed that there had to be a better way and I could swear I actually read about another option.  This isn't really a Grails issue, but more a Spring data binding issue, so with the right set of search terms, my Google searches found what I was looking for (and had actually read before).  Matt Fleming has written an initial posting and then a followup on this subject so you can read more there.  Short answer: define the list as a LazyList which knows what data type to create when it actually does add elements to the list.

The Initial Problem
The initial problem encountered was a NullPointerException because the collection was not initialized with the correct number and type of entries.   My sample solution handled that by creating the Person object first, which contains an empty list.   Then it created each of the Address objects and added those to the list.  Lastly, we did the binding using the whole params map.

Using Matt's solution, you can see the updated Person object below using the LazyList and defining the collection type to Address.

import org.apache.commons.collections.FactoryUtils
import org.apache.commons.collections.list.LazyList

class Person {

    static hasMany = [addresses:Address]
    
    static constraints = {
    }
    
    Person() {
        //addresses = []
    }
    
    String firstName
    String lastName
    List
addresses = LazyList.decorate( new ArrayList(), FactoryUtils.instantiateFactory(Address.class)); String toString() { firstName + " " + lastName + " Addresses:"+addresses } }


The updated controller code is shown below.   No reason to call the bindPerson(params), we just create the Person object with the params map and Grails & Spring do the rest!

def save = {
        //def personInstance = bindPerson(params)
        def personInstance = new Person(params)
        if(!personInstance.hasErrors() && personInstance.save()) {
            flash.message = "Person ${personInstance.id} created"
            redirect(action:show,id:personInstance.id)
        }
        else {
            render(view:'create',model:[personInstance:personInstance])
        }
    } 

Tuesday, September 29, 2009

Client needs to help ensure 'guaranteed delivery' with JMS

We recently received a new defect report claiming that some of our data was not being distributed correctly through JMS. Actually some of the data was not being distributed at all!  That's the first time we have heard that complaint - hey, JMS is guaranteed delivery, right?  Well not so fast...

Apparently we had a 'slow leak' in our connection pool within JBoss MQ.  After a few days of uptime, the server seemed to stop distributing some of our data.  The problem helped expose a deficiency in our JMS client code.  We had not registered an ExceptionListener with our Connection object and as a result, we were not notified of problems creating a new Connection.  I don't know about everyone else but most of the JMS sample client code that I have seen hasn't registered an ExceptionListener so that interface was news to me.   Oh well, another learning experience!

So... JMS guaranteed delivery requires some assistance from the client as well, to help handle connection errors.

Thursday, September 24, 2009

Using HTTPBuilder to call a RESTful web service

Our team is delivering a new RESTful web service and so I figured that I would scratch my Grails itch and create a small application for testing the new web service.   RESTClient looked like it would be a great match and it would be except that I want to display the XML request being passed to the service as well as the XML response returned from the service.   Displaying the XML request is not a problem.  The problem is that RESTClient automatically parses the response data and returns a GPathResult NodeChild object.

After reviewing the documentation for RESTClient and exchanging some emails with the RESTClient developer, he made the suggestion of using HTTPBuilder instead of RESTClient.  The code shown below is the call to HTTPBuilder to call the new service. This topic came up in another posting and you can see an alternative approach here.

Points to notice
  1. The second parameter to HTTPBuilder  is setting the default content type to TEXT.
  2. The request sets the Accept header to 'application/xml'.
  3. HTTPBuilder.RequestConfigDelegate send method sets the content type to XML
  4. Success handler gets response from 'reader.text' and then pretty-prints the result back in to a String for easy display. 


def txnCreate(TransactionSpec txnSpec, String userid, String pswd) {

        def conf = Configuration.get(3)
        if (conf != null) {
           def ppos = new HTTPBuilder( conf.protocol+"//"+conf.server+":"+conf.port, TEXT )
           ppos.headers = [Accept:'application/xml', 'ppos-user':userid, 'ppos-pswd':pswd]
           
           try {
             ppos.request( PUT ) {
                   uri.path = conf.context + conf.url
                   send( XML ) {
                    TransactionSpec (training:txnSpec.training) {
                        delegate.transClassName txnSpec.transClassName
                        delegate.locationNumber txnSpec.locationNumber
                        delegate.currencyCode txnSpec.currencyCode
                        AssociateSpec {
                            delegate.associateNumber txnSpec.associateSpec.associateNumber 
                        }
                        delegate.terminalName txnSpec.terminalName
                        LineItemSpecs {
                            for (item in txnSpec.lineItemSpecs) {
                                LineItemSpec {
                                    sku(item.sku)
                                    quantity(item.quantity)
                                }
                            }
                        }
                        TenderSpecs {
                            for (tender in txnSpec.tenderSpecs) {
                                TenderSpec {
                                    tenderCode(tender.tenderCode)
                                    cardNumber(tender.cardNumber)
                                    expiryDate(tender.expiryDate)
                                    track1Data(tender.track1Data)
                                    track2Data(tender.track2Data)
                                    track3Data(tender.track3Data)
                                    preLockNumber(tender.preLockNumber)
                                    preLockAmount(tender.preLockAmount)
                                    amount(tender.amount)
                                    precision(tender.precision)
                                }
                            }
                        }
                     }   
                   }
                   // success handler
                   response.success = { resp, reader ->
                        // pretty print format the response
                        def stringWriter = new StringWriter()
                        def node = new XmlParser().parseText(reader.text);
                        new XmlNodePrinter(new PrintWriter(stringWriter)).print(node)
                        return stringWriter.toString()
                   }
                   // failure handler
                   response.failure = { resp ->
                      return 'Response status='+resp.status
                   }
             }
           } catch(Exception e) {
               log.error("Caught exception:", e)
               return e.toString()
           }
        }
    }

Thursday, September 17, 2009

Blogger Minima template wastes space

I just recently started blogging and so I am trying to figure out what looks good and how to get my blog to look better.   I choose the Minima template but noticed that the 'gutters' seemed too wide down the left and right side of the page.  Postings with code seemed to caused a lot of the line to wrap and it just didn't look very good. After seeing some other blogs that didn't appear to have that problem, I posted comments asking, "How'd you do that?".   Someone suggested adjusting the template and so I took his advice.   Below is a just a couple minor tweaks I made to the template and the code examples look much better and the gutters are not nearly so wide!

This all coming from a developer who has spent most of his career in the middle-tier and server-side of applications.   For those of you very familiar with CSS, you can probably stop reading NOW.

Updating the Minima template to increase the posting area
  1. Go to Layout --> Edit HTML page
  2. Click the 'download template' link to make a backup copy of your template, for safe keeping
  3. In the editable textarea, make the following changes to the style sheet entries: 
    • outer-wrapper width change from 660px to 1000px, 
    • main-wrapper width change from 410px to 700px 
    • footer width change from  660px to 1000p
  4. Click the 'preview' button to see the changes
  5. If you are happy with the new spacings, click the 'save template' button.

Wednesday, September 16, 2009

Example Grails data binding for one-to-many association

Data binding is an integral aspect of all web applications.  The responsibility for the data binding generally falls within the scope of the controller.  In this following example,  I will show all the 'moving parts' that are needed to easily bind a domain object with a one-to-many association within Grails. 

I looked around for a simple example of data binding to handle a one-to-many association and never really found a full example so I decided to create an example and share it.  The example uses a Person domain object with a one-to-many association of Address objects.   All of the data is captured on a single screen.   The example uses DHTML or Dom scripting to dynamically create multiple address form fields and submits all the data from one screen.


Domain objects: Person & Address
The Person constructor contains just a first and last name.  Notice the Address field implements Serializable and also implements the hashCode() and equals() methods as required for objects that are to be placed in a collection.

class Person {
    static hasMany = [addresses:Address]
    static constraints = {
    }
    
    Person() {
        addresses = []
    }
    
    String firstName
    String lastName
    List<Address> addresses
    
    String toString()  {
        firstName + " " + lastName + " Addresses: "+addresses
    }
    
}


class Address implements Serializable {
    static constraints = {
    }
    
    String street
    String city
    String zip
    
    String toString() {
        street + "\n" + city + ", " + zip
    }
    
    int hashCode() {
        street?.hashCode() + city?.hashCode() + zip?.hashCode()
    }
    
    boolean equals(o) {
        if (this.is(o)) return true
        if (!(o instanceof Address)) return false
        return street?.equals(o.street) && (city?.equals(o.city)) && (zip?.equals(o.zip))
    }    
}


Sample screen shot of create person screen

Below is a snapshot of a the create.gsp screen after clicking the 'Add Address' button twice and filling in the data.  The 'Add Address' button calls a javascript function (see further below) to create an additional address row in the address table.  (I wanted to provide the entire page .gsp source but could not get it formatted correctly - if anyone has suggestions, I would appreciate hearing from you.)  



Add Address javascript function

Below is the javascript function called from the 'Add Address' button on the form.  The important part here is that use of the hidden field, addrCount, to add an index to the address form field names and ids.

function addAddr(tblId)
{
  var tblBody = document.getElementById(tblId).tBodies[0];
  var newNode = tblBody.rows[0].cloneNode(true);
  var count = parseInt(document.getElementById("addrCount").value);
  count++;
  document.getElementById("addrCount").value = count;
  var cells = newNode.cells;
  for (var i=0; i
      if (cells[i].firstElementChild.id.indexOf("street") != -1) {
          cells[i].firstElementChild.id = "addresses["+(count-1)+"].street";
          cells[i].firstElementChild.name = "addresses["+(count-1)+"].street";
      } else if (cells[i].firstElementChild.id.indexOf("city") != -1) {
          cells[i].firstElementChild.id = "addresses["+(count-1)+"].city";
          cells[i].firstElementChild.name = "addresses["+(count-1)+"].city";
      } else if (cells[i].firstElementChild.id.indexOf("zip") != -1) {
          cells[i].firstElementChild.id = "addresses["+(count-1)+"].zip";
          cells[i].firstElementChild.name = "addresses["+(count-1)+"].zip";
      }
  }
  tblBody.appendChild(newNode);
} 
 
Here is the initial statement for the hidden addrCount index field.

<g:hiddenField name="addrCount"   value="1" />


PersonController
When the form is submitted, the save action is called.  I have created a helper method, bindPerson(), to handle the data binding.  The method iterates 'addrCount' times, creating a new Address object, calling the bindData() method provided by Grails with the params starting with the value 'addresses[i]'.  Lastly, the address is added to the collection and the Person object is returned.

The controller code below works party because my constructor for the Person object initializes the addresses field to an empty list.   Other options might be to create a list in the controller and then set the addresses field to that list, or keep the initialized list and call personInstance.addToAddresses(addr).

class PersonController

  def save = {
        def personInstance = bindPerson(params)
        if(!personInstance.hasErrors() && personInstance.save()) {
            flash.message = "Person ${personInstance.id} created"
            redirect(action:show,id:personInstance.id)
        }
        else {
            render(view:'create',model:[personInstance:personInstance])
        }
    }
    
    def Person bindPerson(params)  {
        def personInstance = new Person()
        def count = params.addrCount.toInteger()
        for (int i=0; i<count; i++) {
            def addr = new Address()
            bindData(addr, params["addresses["+i+"]"])
            personInstance.addresses[i] = addr
        }
        personInstance.properties = params  
        return personInstance
    } 
 
Results
Here you see the results on the show.gsp screen.


Wrap up:
  1. First you need to setup your domain objects, including implementing Serialization, hashCode() and equals(),  where required.
  2. Form fields need to be name using index, like addresses[0].city or addresses[3].zip
  3. Finally, the controller needs to bind the association data using the indexed fields.
Note:  After writing this up, I finally realized why my Address object looked strange, it is missing a field for state.  While important for a correct Address object, it isn't absolutely necessary for this example, so I decided not to go back and add it in.

Note: Please see an improved solution in Part 2

I hope this helps and as always, constructive criticism is appreciated! Due to the large number of requests, I have created a site over at Groovy sites and you should be able to download from here.  Download site is https://sites.google.com/site/mikesgroovystuff/

Sunday, August 30, 2009

Simple Groovy mistake had me wondering what the heck was going on!

While 'scatching my programming itch' late one night last week, I made a small mistake which didn't present itself until runtime.   When it did occur, it made we wonder what the heck was happening.  After fixing my mistake,  I just had to figure out what was really happening.

Minor background for readers new to Groovy:

Groovy provides language support to make the routine tasks on collection types easier than Java programmers are used to.  For example, creating a map is as easy as:
def map = [key1:valueX, key2:valueY]
which creates a map with two entries: key1 maps to valueX and key2 maps to valueY. The fun starts with the many ways of accessing the maps data. The following are all valid ways of retrieving valueX from the map:
def value = map.get('key1')
def value = map['key1']
def value = map.key1

The first example looks just like Java. The second and third examples are provided by the Groovy language. Now, on to the mistake.

Can you guess what happens?
I have created a very small subset of the code to show the bug.  The actual code resides in a Grails controller method that gets invoked on a form submission.   In the submitted form, I use DOM scripting to dynamically create multiple line items for a transaction.

Take a look at the excerpt below and see if you can tell what happens when it executes.

def params = [itemCount:'3']  // this was really form data submitted
for (int i=0; i<params.itemCount; i++)
    def lineItem = new LineItem()
    bindData(lineItem, params["lineItem["+i+"]"])
    transactionInstance.lineItem[i] = lineItem
}

Anyone see the error?
The for loop looped about 10-12 times and then stop even though there were only 3 lineItems submitted on the form.  Other times, the loop ran forever.  The problem: the value of the itemCount entry is a String and not a Integer, which resulted in a strange comparison (more on that later).   The fix was easy, convert the String to an Integer.

def count = params.itemCount.toInteger()
for (int i=0; i<count; i++)

But why did the loop behave the way it did?

Operator overload for < (less than)
The next step is to figure out what Groovy's doing.   The operator <  is basically doing a comparison, so what happens when you try either of the following:

i.compareTo(params.itemCount)
params.itemCount.compareTo(i) 
The answer is, both throw a ClassCastException indicating that an Integer can not be cast to a String and vice versa.  So how is Groovy coercing this comparison?  Time to decompile the code to get to the bottom of this. 

Decompile the code!
Below is a snippet of the decompiled code where you can see it calls the ScriptBytecodeAdapter compareLessThan method with both of the arguments as Objects, so we get an identity comparison!

  for(Integer i = $const$0; ScriptBytecodeAdapter.compareLessThan(i, acallsite[1].callGetProperty(params));)

Friday, August 28, 2009

Finally got a blog!

Talk about being "late to the party", I have finally setup a blog. I plan on blogging on Java, Groovy & Grails and other topics that I would like to share while I "scratch my programming itch".
Hopefully some of my posts will be of interest to others and help them, as so many others have done for me. Please feel free to comment. Life's a learning experience!