James Williams
LinkedInMastodonGithub

Creating Ratpack Apps with the Gradle Application Plugin

Currently, there are two ways to run a Ratpack app, you can build and run the ratpack executable or you can use the runapp groovy script. Both methods are ok but I wanted a way to be able to easily move apps between computers and make sure I didn't miss a jar. I started poking around in the Gradle documentation and found what I needed in the application plugin.

Gradle's Application plugin provides tasks that are common to an application's lifecycle:

  • run, which runs the app,
  • installApp, which installs the app into a directory,
  • startScripts, which creates OS-specific scripts to start the app, and
  • distZip, which creates an archive of the app directories.

All of this functionality is provided mostly for free by including the following code in your build.gradle file:

    apply plugin: 'application'

    mainClassName = '<package and name of your class>'

In lieu of installing Ratpack to my local Maven repository and assuming anyone I sent a distro of the app would too, I dropped the file into a lib directory and created a flat repository for that directory.

    repositories {
        flatDir name:'lib', dirs:'lib'
        mavenCentral()    
    }

The Ratpack build jar does not contain all of its dependencies so I copied them over to my dependencies gradle block. You could just as easily drop the dependencies into your flat repo and reference them from dependencies. I decided to let Maven do the heavy lifting and retrieve them at run-time. I set up my source directories in the default Maven style and I was ready to go. gradle run actually started the app and I was able to respond to routes.

However, things failed tragically whenever I tried to access a public file or template from either the installed version, created using gradle installApp, or the extracted archive, created using gradle distZip. I quickly realized that it was because I naively thought gradle was going to package everything in the root directory. After a little wrangling, I happened upon the required incantations to make it work. The following code explicitly copies the template and public directories to the installed app and archive.

    installApp {
        into('build/install/'+applicationName){
            from ('templates').into '/templates'
        }
        into('build/install/'+applicationName){
            from ('public').into '/public'
        }
    }
    distZip {
        into(applicationName){
            from ('templates').into applicationName+'/templates'
        }
        into(applicationName){
            from ('public').into applicationName+'/public'
        }
    }

You can view the whole file here.