Downloading URL or copying Files using Categories in Groovy

Over the weekend I needed to write a script in groovy which first downloaded a file from a remote webserver then, if that file was retrieved & different copy it to a final location.

Now as Groovy is based on Java you could have done this in the usual manner but I wanted to find a groovy way & found this method which is way more elegant.

The way I found to do this is by using a category to extend the << operator. To do this we create a new class at the end of the script and add a method implementing the operator with the appropriate types.

First we want to implement File << Url which will write the content of a url to a file:

    def static leftShift(File file, URL url) {
       url.withInputStream { is->
            file.withOutputStream { os->
                def bs = new BufferedOutputStream( os )
                bs << is
            }
        }
    }

Now we have that we can simply download the remote file with a few lines. Here we set a URL of a remote rss feed, the file we want to download it to then, using our new category we do the actual copy.

def source = new URL( 'http://trainwatch.co.uk/forums/feed.php?mode=topics' )
def target = new File( 'trainwatch.rss' )

use( FileBinaryCategory ) {
  target << source} 

Extending the category to copy local files is just as simple, just overload leftShift with a File as the source:

    def static leftShift(File dst, File src) {
         src.withInputStream {
           is -> dst.withOutputStream {
          os -> def bs = new BufferedOutputStream( os )
          bs << is
        }
      }
    }

Now we can extend our example above to copy the file elsewhere:

def source = new URL( 'http://trainwatch.co.uk/forums/feed.php?mode=topics' )
def temp = new File( '/tmp/trainwatch.rss' )
def target = new File( 'trainwatch.rss' )

use( FileBinaryCategory ) {
  // Download the rss from the remote site to the temp file
  temp << source

  // Imagine we do some tests here & then copy the file
  target << temp
  } 

Here’s the full category class, all I do in standalone scripts is put this at the end:

class FileBinaryCategory {
  def static leftShift(File file, URL url) {
    url.withInputStream { is->
      file.withOutputStream { os->
        def bs = new BufferedOutputStream( os )
        bs << is
      }
    }
  }
  def static leftShift(File dst, File src) {
    src.withInputStream {
      is -> dst.withOutputStream {
        os -> def bs = new BufferedOutputStream( os )
        bs << is
      }
    }
  }
 }

Now this doesn’t just have to be used for file io either. One usecase I had was to take a list of command line arguments to pass on to a command. Simple except if the list contained another list then I had to flatten the two lists before passing them to the command. Now usually you’d flatten them but what if you had a null or even an empty string? In this case this was possible but I had to strip them out first.

So how to do it? well here’s the category:

class ArgumentsCategory {
 def static leftShift( List list, String arg ) {
   if( arg!=null && !arg.empty ) {
     list.add(arg)
   }
 }

 def static leftShift( List list, List args ) {
   args.each{
     leftShift( list, it )
   }
 }

 def static leftShift( List list, File file ) {
    list.add( file.canonicalPath )
 }
}

Now here we’ve defined three extensions to the << operator. For all three the left and side is the list were appending to while the right is either a String, List or File. For a string it simply appends it. For the file it’s the canonical Path thats appended otherwise we run through each element of the list as if it’s the same command.

Now we can just use this in a similar manner as before:

def src = new File( 'a.tiff' )
def dst = new File( 'a.png' )
def parms = [ '-rotate', '-90', '-resize', '200x200' ]

def args = []
use( ArgumentsCategory ) {
  args << src
  args << params
  args << dst
}

The above code would generate a single list of command arguments for the ImageMagick convert command.

Using groovy to generate java sources in maven

In this article I’ll briefly cover how to use Groovy to generate java sources as part of a maven build.

There are times when you need to generate Java source which is either repetitive, based on external data or both. Now there are tools out there for some tasks like this, but for most simple tasks running a groovy script is more than enough.

Now for the purposes of this article, I’m going to presume we need an enum which represents the cards in a Standard deck of cards, one entry per card.

First we need to create our groovy script. Now the best place to put this is in the src/main/script directory of your project. Here’s the script for this (writing groovy is not in scope for this article) which goes into the file src/main/script/CardDeckGen.groovy:

class CardDeckGen {

    public generate( project, String packageName, String className ) {

        // Where to write the classes
        File targetDirectory = new File( project.build.directory + '/generated-sources/groovy' )

        // The directory to write the source to
        File packageDir = new File( targetDirectory, packageName.replace( '.', '/' ) )

        // Now to create our enum
        def out = []
        out<<'package '+packageName+';\n'
        out<<'public enum '+className+' {\n'

        // We have four suits
        def suit = [ 'D', 'S', 'H', 'C']

        // Each suit has A, 2-9, T, J, Q & K
        def rank = [ 'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K' ]

        // For each suit, write an entry for each card
        suit.eachWithIndex {
            s, i -> def indent = i==0 ? '    ' : ',\n    '
            rank.each {
                r -> out<< indent + s + r
                indent = ','
            }
        }

        // Mark the end of the enum list
        out<<';\n'

        // Finish the enum class
        out<<'}\n'

        // Convert the array into a string
        StringBuilder sb = new StringBuilder()
        out.each { sb.append(it) }

        // Now write the source, ensuring the directory exists first
        packageDir.mkdirs()
        new File( packageDir, className + ".java" ).write( sb.toString() );
    }

}

Next we need to get maven to run this script, so in pom.xml we first need to make sure we are using the correct version of the jdk – 1.5 or 1.6 would be enough:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>

Next groovy, here we add the gmaven-plugin which runs a small code snippet calling our class:

<plugin>
    <groupId>org.codehaus.groovy.maven</groupId>
    <artifactId>gmaven-plugin</artifactId>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
                <scriptpath>
                    <element>${basedir}/src/main/script</element>
                </scriptpath>
                <source>
                    CardDeckGen cdg = new CardDeckGen()
                    cdg.generate( project, 'uk.org.retep.example', 'CardDeck' )
                </source>
            </configuration>
        </execution>
    </executions>
</plugin>

Now that’s enough to generate our enum, however maven doesn’t know about it so it wont compile. To do that we need to add it so the compiler picks it up:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-groovy-sources</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>${project.build.directory}/generated-sources/groovy</source>
                </sources>
            </configuration>
        </execution>
    </executions>

Now you can run maven.mvn clean install
If all goes well you’ll get a maven artifact containing an enum:

package uk.org.retep.example;
public enum CardDeck {
    DA,D2,D3,D4,D5,D6,D7,D8,D9,DT,DJ,DQ,DK,
    SA,S2,S3,S4,S5,S6,S7,S8,S9,ST,SJ,SQ,SK,
    HA,H2,H3,H4,H5,H6,H7,H8,H9,HT,HJ,HQ,HK,
    CA,C2,C3,C4,C5,C6,C7,C8,C9,CT,CJ,CQ,CK;
}

This was a simple example but quite a powerful one. I’ve used this technique for generating more complex versions of Card’s to classes based on data contained within RFC’s (STRINGPREP etc).

For help on writing Groovy stripts take a look at the Groovy Getting Started Guide.

%d bloggers like this: