Skip to content


Groovy to the rescue, in under 70 lines of code

Summary: If you don’t want to read the text and want to just jump into the code, I will basically show how I used Groovy to do some database access / sql, a tiny bit of regular expression checks, and an outline of how to browse through directories and copy files.


Update: I’ve updated the code below to be a bit more “groovier” in response to the comments, you can check out the changes here


Ok this post is about a scenario that made me promote more Groovy (the language of course) usage into my day to day programming activities.

The other day at work, we had to deal with digging up some files from an ancient document management system in order to do manual semi-random data quality checks between the files in the DMS (mostly PDFs and some photos) and the data in our database. (If you are intrigued to know why we are doing this, lets just say we have different applications serving the same purpose but each one has it’s own set of users who don’t like to communicate with each other, it’s a long story…).
In any case, we wanted to do some random checks between the documents and the data in the other application’s database, but in order to find those documents we could either ‘face palm’ our way through the ancient GUI of the document management system, or use a pointer to the file system location of the files we want, which is stored in yet another database.(Yes, yes, i know…)

So basically we started by doing the following steps in order to get the documents, (I will call our database DB1 and the database with the pointer to the file system DB2)

  1. Using the group name we are checking, we need to grab an ID from DB1
  2. Use the ID we got from DB1 to query DB2 for the path to the directory containing the documents we want
  3. Browse to that directory and access the required documents (or preferably copy them locally for further investigations)

You can see how this gets boring after a couple of runs…

Normally, I would write a small java utility application to do the above three steps, and just supply it with the group name and let it do the magic, but given that I’ve been using groovy in my adventures with Grail lately, I thought I would give it a shot outside of grails an try to solve this problem (plus it’s a good way for me to learn more about groovy)

As it turns out, I managed to write a groovy script to do all the above in less than 70 lines of code (unless you count my OCD of having symmetrical spacing between the first and last line of a method), so here is a rundown of what I did, hopefully you will find it useful.
Some of the areas I will touch upon include

  • sql in groovy
  • file manipulation in groovy
  • sexiness of groovy

First off, the snippet below outlines the skeleton of the operations mentioned above in groovy (Note: since this is an internal use only script I did not bother with catching exceptions which is good for rapid development, not so good if you go into production without proper checks)

    static void getThemOverHere(documentGroup, dbConn1, dbConn2) {
        // get the group id from the first database
        def groupId = getIdFromGroupName(documentGroup, dbConn1)
        if (groupId) {
            //using this id, get the path to the docs from the second database
            def pathToDocFolder = findPathToDocs(groupId, dbConn2)
            if (pathToDocFolder) {
                //use the ant task to move the files to my desktop from the path
                moveDocumentationToMe(pathToDocFolder, groupId)
            } else { println "Path to docs for group ${groupId},  not found in DB2" }
        } else { println "Group Id for group ${documentGroup},  not found in DB1" }
    }

Ok there is nothing groovy about the above, except for saving those extra == null checks for the returned strings (groupId and pathToDocFolder).

getIdFromGroupName and findPathToDocs are similar in implementation, the only difference is that I pass a different groovy.sql.Sql objects ot each one.

    static String getIdFromGroupName(documentGroup, dbConn) {
        def grpId = null
        def row = dbConn.firstRow(
                "select group_id from doc_groups where group_name = ${documentGroup}")
        if(row)
            grpId = row[0]
        return grpId
    }

    static String findPathToDocs(groupId, dbConn) {
        def pathToDocs = null
        def row = dbConn.firstRow(
                "select full_path from documentation_library where group_id = ${groupId}")
        if(row)
            pathToDocs = row[0]
        return pathToDocs
    }

As you can see, dealing with sql is really easy (of course keep in mind I’ve completely ignored using try/catch because I can :) ), and I won’t even start comparing how this would look if implemented in Java (don’t get me wrong here, I love Java but the above is pure win)

Ok, so now that I have the path to the folder location that contains the files I am interested in, I am going to do the following steps in order to get the files to my machine:

  • Create a directory on my desktop (if it doesn’t already exist)
  • Find a sub directory within the path passed to me, the sub directory I’m interested in will contain the group id I started with somewhere in the middle of it’s name
  • Create a sub directory within the directory I created on my desktop with the same name as the directory I found
  • Use an Ant task to copy the files over to my machine.

Below is my file moving method (with some comments for clarifications)

    static void moveDocumentationToMe(pathToFolder, groupId) {

        // move everything to my desktop, of course i could have passed as an argument,
        // but i don't really care for now
        FileSystemView filesys = FileSystemView.getFileSystemView()
        def destinationDir = filesys.getHomeDirectory().getCanonicalPath()
        //paranoia :)
        if (!destinationDir.endsWith(File.separator))
            destinationDir += File.separator
        destinationDir += "Results"

        //create the directory if it doesn't exist yet
        def destDirFile = new File(destinationDir);
        if (!destDirFile.exists())
            destDirFile.mkdir();

        //define a new AntBuilder in order to use
        //the copy task to copy the files
        def antBuilder = new AntBuilder()

        File file = new File(pathToFolder)
        for (f in file.listFiles()) {
            // the path can contain many folders (don't ask why)
            // The ones I want have the Group ID somewhere in the middle
            // of the file name (thank god the naming convention is consistent)
            if (f.name ==~ ".*${groupId}.*") {
                //i need to get all files in this directory
                def groupFolder = new File(pathToFolder + File.separator + f.name)
                //and put them in their own direcoty in my destinationDir
                def destSubDir = new File(destinationDir + File.separator + f.name)
                if (groupFolder.listFiles()) {
                    //create the directory if it doesn't exist
                    if (!destSubDir.exists())
                        destSubDir.mkdir();
                    def targetPath
                    for (toMove in groupFolder.listFiles()) {
                        targetPath = destSubDir.getCanonicalPath() + File.separator + toMove.name
                        antBuilder.copy(file: "${toMove.getCanonicalPath()}", tofile: "${targetPath}")
                    }
                }
            }
        }
    }

To glue everything together, I created a main method that accepts the document group as a command line argument. (sorry db connections are hardcoded & fictional, but I am really connecting to one sybase database and another mssql database)

    static main(args) {
        if (args.legth != 1) {
            println "Gimme Document Group Name as Argz!!"
        } else {
            def dbConn1 = Sql.newInstance("jdbc:jtds:sybase://server1:4100/db1", "usr", "pwd", "net.sourceforge.jtds.jdbc.Driver")
            def dbConn2 = Sql.newInstance("jdbc:jtds:sqlserver://server2:1433/db2", "usr2", "pwd2", "net.sourceforge.jtds.jdbc.Driver")
            getThemOverHere(args[0], dbConn1, dbConn2)
            dbConn1.close()
            dbConn2.close()
        }
    }

Of course you will need to add the following to your imports (The FileSystemView is only used to get the path for the Desktop, not sure if this is the best way though)

import groovy.sql.Sql
import javax.swing.filechooser.FileSystemView

Now the only boring task that remains is the actual manual checking of the data in the PDF files and photos against the database, but hey at least I got rid of manually querying 2 databases and fetching files through heaps of directories on a remote server.

Posted in Programming.

Tagged with , , , .


7 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Thai Dang Vu says

    I think spring jdbc code is not much longer and you need one more xml file to define the datasource (I have to say that I never remember the syntax of spring xml configuration files).

  2. Dave says

    You’ll be even more excited once you start programming Groovy in Groovy, and not Java.

  3. Caligula says

    Why are getIdFromGroupName and findPathToDocs not refactored, and 2x+ as long as they need to be? AntBuilder to copy a file? new File(“/tmp/otherFile”) << new File("/tmp/someFile").text… and besides not using .each, calling something "toMove" when it's actually being copied is a little misleading.

  4. Caligula says

    (Or use commons, which is a bit cleaner than AntBuilder for a file copy, IMO.)

  5. Caligula says

    (And check out groovy’s file traversal methods, also cleaner. I think I’m done now… but spend some time checking it out, you’ll be glad you did.)

  6. omarji says

    Thanks for the inputs, as you have mentioned I am sure it would be much better to do it in a Groovier way, but I still need some time to transition between the two.
    As for the file traversal methods, I just checked them out and they seem much cleaner than what I’ve used. But as far as the copy method goes, using << File(“……”).text works fine for text files, but I doesn’t work with pdf files and images I’m working with(at least I couldn’t make it work), and as far as using Ant Builder, the reason was that it was much faster at copying files larger than 5 megs across the network.

  7. A B says

    and use @Grab to use the Apache Commons(that Caligula suggested) automatically in groovier way and you are through to use it.



Some HTML is OK

or, reply to this post via trackback.