Archive for the Category 'ant'

Clustering Grails

Saturday, December 19th, 2009

I did a talk on Grails clustering at the Groovy & Grails eXchange 2009 in London last week and wanted to put up the slides and the sample application and clustering scripts. This was the third time I’ve given a version of this talk (first at a Boston Grails meetup and again at SpringOne 2GX) so I’m way overdue getting this up.
(more…)

Using GORM outside of Grails part 2 – Swing

Sunday, September 07th, 2008

In an earlier post I wrote about getting GORM to work outside of Grails. It worked, but wasn’t really usable since it could only execute script files, so its usefulness was pretty limited in a real application. Greg Bond replied on the mailing (here and here) with some great enhancements that allowed it to work without script files. So I fleshed that out some more and now have a working implementation and even a Swing application to demonstrate.

The key was that Greg used grails compile to generate his domain class files. I’d just been using the groovyc Ant task, and when I looked at what the Grails compile script the difference turned out to be that Grails uses a Grails-aware subclass of Groovyc, org.codehaus.groovy.grails.compiler.GrailsCompiler. It allows you to specify a resourcePattern attribute to point at the domain class .groovy files for special treatment.

So now instead of one sample project there’s three. One is the gorm standalone project, which creates a jar (gorm_standalone.jar) containing GormHelper which bootstraps GORM. The other two are the sample application, split into GORM domain classes and the Swing application. The domain class application contains the domain class .groovy files plus DataSource.groovy, and most importantly an Ant script that builds a usable jar (domainclasses.jar). The Swing application uses gorm_standalone.jar and domainclasses.jar as libraries and displays a simple UI showing the results of database queries.

Here’s a quick screen shot:

You can download the sample apps here:
GORM standalone app
Sample app domain class app
Sample Swing app

Object-Oriented Ant Build Files

Monday, August 07th, 2006

Powerful features in Ant 1.6 including presetdef, macrodef, and import make it simple to keep common tasks in common locations and simplify the creation of a project’s build script. Further, although Ant scripts are not programs in the traditional sense, these features do allow quasi-OO techniques.

These aren’t new features – Ant 1.6 was released almost three years ago – but it’s always good to be reminded of cool features.

Build files for Java projects tend to contain the same core sets of actions:

  • compile source classes
  • compile test classes
  • run unit tests
  • build javadoc
  • package files into JARs, WARs, EARs, etc.

I find that using Spring and Hibernate in many projects I also generate Hibernate *.hbm.xml files using XDoclet, and I’m also starting to formalize best practices for running test coverage and generating quality metrics as part of the continuous build process, so I’m adding the common elements of those tasks also.

PresetDef

PresetDefs allow you to set default values and even default child elements in existing targets. For example, here’s my PresetDef for the copy target:

<presetdef name='def.copy'>
  <copy preservelastmodified='true' includeEmptyDirs='false' />
</presetdef>

I can override either preservelastmodified or includeEmptyDirs when I call def.copy, but if I leave those attributes out the default values are used. This is essentially like having a protected accessor in a base class.

PresetDefs can contain default child elements too, for example:

<presetdef name='def.junit'>
  <junit
    fork='true'
    haltonfailure='${test.haltonfailure}'
    haltonerror='${test.haltonerror}'
    printsummary='true'>
    <formatter type='plain' usefile='false' />
    <formatter type='xml' />
  </junit>
</presetdef>

In this case I’m defaulting to fork the jvm for testing, I’m using build properties to specify whether to fail the build on error or failure, and I’m including two formatters, one to stdout and one to an XML file.

Declaring a PresetDef creates a new pseudo target whose XML element name is the name attribute.1 Here’s a simple target that’s used in conjunction with compiling Java source; typically you’ll want to copy all non-Java resources from the source path to the class path, e.g. image files, property files, etc. after compiling:

<target name='copy.resources' depends='prepare'>
  <def.copy todir='${dir.classes.src}'>
    <fileset dir='${dir.src}' excludes='**/*.java'/>
  </def.copy>
</target>

In this example I’m using def.copy primarily because I want to maintain the modification date of the files. This wouldn’t make sense if I were changing the file while copying it (e.g. if I were replacing tokens) but I do want to preserve dates in this case so I know that the correct versions of the files were copied.

MacroDef

The primary motivation for adding MacroDef to Ant was to avoid the cost of AntCall. For small projects you probably wouldn’t notice the time and resource costs of AntCalls but for a large and complex project, reloading/reparsing the build script and reinitializing everything can slow down a build significantly. So MacroDef was added to address the need to perform common sequences of tasks with parameter passing, similar to calling a method in a programming language, but in a lightweight fashion.

Here’s a sample MacroDef for running JUnit tests. This one is fairly simple with just two subtasks – running the tests and generating HTML reports from the XML formatter output. There are two attributes, maxMemory (with a typical default value) and report.dir, and two required child elements filesets and classpathElements:

<macrodef name='unitTest'>
  <attribute name='maxMemory' default='-Xmx256m'/>
  <attribute name='report.dir' />
  <element name='filesets' optional='false' />
  <element name='classpathElements' optional='false' />
  <sequential>

    <def.junit>

      <classpath>
        <classpathElements />
      </classpath>

      <jvmarg value='@{maxMemory}' />

      <batchtest todir='@{report.dir}'>
        <filesets />
      </batchtest>
    </def.junit>

    <junitreport todir='@{report.dir}'>
      <fileset dir='@{report.dir}'>
        <include name='TEST-*.xml' />
      </fileset>
      <report format='frames' todir='@{report.dir}' />
    </junitreport>

  </sequential>
</macrodef>

attributes and elements allow simple and complex ‘parameter’ passing. An attribute is a string parameter, like a property, except that the syntax for resolving its value uses an ‘@’ instead of ‘$’ to avoid collisions with build properties. elements allow you to pass entire XML elements (arbitrarily complex) into a MacroDef. Note that Ant does validate the usage and the build will fail if non-optional child elements are missing.

Here’s a sample usage of this MacroDef:

<target name='def.test'
  depends='def.clean, xdoclet.hibernate, def.compile.tests'
  description='run all unit tests'>

  <delete dir='${dir.testresult}'/>
  <mkdir dir='${dir.testresult}'/>

  <unitTest report.dir='${dir.testresult}'>
    <filesets>
      <fileset dir='${dir.test}'>
        <include name='**/*Test.java'/>
        <exclude name='**/Base*Test.java'/>
      </fileset>
    </filesets>
    <classpathElements>
      <path refid='classpath.test'/>
      <pathelement location='${dir.classes.test}'/>
    </classpathElements>
  </unitTest>

</target>

And to see how Ant inserts default values from the PresetDef and parameters from the MacroDef, here’s the equivalent Ant code not using PresetDef or MacroDef:

<target name='def.test'
  depends='def.clean, xdoclet.hibernate, def.compile.tests'
  description='run all unit tests'>

  <delete dir='${dir.testresult}'/>
  <mkdir dir='${dir.testresult}'/>

  <junit
    fork='true'
    haltonfailure='${test.haltonfailure}'
    haltonerror='${test.haltonerror}'
    printsummary='true'>

    <formatter type='plain' usefile='false' />
    <formatter type='xml' />

    <classpath>
      <path refid='classpath.test'/>
      <pathelement location='${dir.classes.test}'/>
    </classpath>

    <jvmarg value='-Xmx256m' />

    <batchtest todir='${dir.testresult}'>
      <fileset dir='${dir.test}'>
        <include name='**/*Test.java'/>
        <exclude name='**/Base*Test.java'/>
      </fileset>
    </batchtest>
  </junit>

  <junitreport todir='${dir.testresult}'>
    <fileset dir='${dir.testresult}'>
      <include name='TEST-*.xml' />
    </fileset>
    <report format='frames' todir='${dir.testresult}' />
  </junitreport>

</target>

As with any code reuse strategy (e.g. base classes or JSP includes) the biggest benefit is centralization. If you copy and paste and need to make changes in the common code, you need to find all instances and make multiple identical changes, risking missing some. But with intelligent code reuse, when you change the code in the base class or included file, or for Ant build files in a PresetDef or MacroDef, and all users immediately see the changes.

Import

So you’ve created a bunch of useful PresetDefs, MacroDefs, Targetss, etc. but how do you use them in your various build scripts? Ant build files are just XML, so the traditional way was to use entity includes. This basically worked but was very inflexible. In Ant 1.6 you can use import to intelligently include a partial or complete build script. It’s intelligent inclusion because you can redefine (override) targets or other elements and you can even continue to use or call the overridden versions.

Here’s a complete sample build script for a project called ‘common’ that contains shared code common to many projects. It imports a file called standard.ant2 located here, which contains the above examples and a few other targets, paths, and property definitions:

<?xml version='1.0' encoding='UTF-8'?>

<project name='common' basedir='.' default='jar'>

  <!-- optional properties file for developer overrides -->
  <property file='build.properties' />

  <import file='standard.ant' />

  <target name='xdoclet.hibernate'
    description='generates hibernate descriptors'
    depends='def.compile.java'>

    <xdocletHibernate>
      <classpathElements>
        <fileset dir='lib/xdoclet/'>
          <include name='*.jar' />
        </fileset>
        <path refid='classpath.libdir' />
      </classpathElements>
      <filesets>
        <fileset dir='${dir.src}' includes='**/model/**/*.java' />
      </filesets>
    </xdocletHibernate>

  </target>

  <target name='jar'
   depends='def.clean, xdoclet.hibernate, def.compile.tests'>

    <jar jarfile='build/common.jar'>
      <fileset dir='${dir.classes.src}' />
      <fileset dir='${dir.classes.test}' />
    </jar>
  </target>

  <target name='init.dependencies' depends='check.javadoc.uptodate'/>

  <target name='javadoc'
    depends='prepare'
    unless='javadoc.uptodate'
    description='Build Javadoc'>

    <delete dir='${dir.javadoc}' />
    <mkdir dir='${dir.javadoc}' />

    <def.javadoc doctitle='Common'/>
  </target>

  <target name='test' depends='def.test' description='Run unit tests' />

  <target name='all' depends='jar, test, javadoc' description='Run unit tests' />

</project>

This is a pretty small build file considering what it does. If you run the all target it will do the following:

  • delete the previous build directory
  • compile Java source
  • compile test Java source
  • generate Hibernate .hbm.xml descriptors for classes in any package whose name contains “model”
  • build a JAR file with source and test classes (to include reusable test base classes), Hibernate descriptors, and all other non-Java resouces in the src and test directories
  • run unit tests
  • generate HTML reports from JUnit XML formatter output
  • build Javadoc

This certainly isn’t all that apparent just by looking at the build script, but that’s a side effect of any code reuse.

Note that Ant properties cannot be changed once they’ve been set, so the statement order is important. First a build.properties file is loaded (if it exists) to give the developer a chance to set custom values. Then standard.ant is imported and any property values that weren’t set from the command line via -D parameters or in build.properties are set.

So you can see that it’s quite simple to extract common build functionality into libraries of reusable components to very quickly create build scripts with significant functionality, with only a minimum of customization. Even the sample build script that I’ve show could be a lot smaller if all projects have the same basic structure (one Java source directory in src/, one test directory in test/, JARs in lib/, etc.). You could easily push even more than I have into standard.ant (and other included files) and have build scripts that contain little more than the title for your Javadoc and the name of your JAR file.

  1. Note that the naming convention of prefixing “def.” to the PresetDef is mine and not a requirement of Ant.[back]
  2. My naming convention for Ant includes is to use “ant” as the file extension to differentiate between including build.xml files and included *.ant files[back]

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.